OpenZeppelin's repo uses abstract contracts rather than interfaces when defining structs and enums to not violate any best practices.

The two examples, [`IGoverner.sol`][1] and [`IGovernorCompatibilityBravo.sol`][2], define an enum and a struct element (respectively). However, they are defined as abstract contracts rather than interfaces- and still use the same naming convention as normal interfaces. None of OpenZeppelin's "real" interfaces define enums or structs directly in them, likely in consideration of best practices.

Here's my example code

```solidity
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

abstract contract IPersonContract {
    struct Person {
        string firstName;
        string lastName;
        uint8 age;
        uint256 favNumber;
    }

    function getPerson(uint256 index) public view virtual returns (Person memory);

}

contract PersonContract is IPersonContract {
    Person[] private _people;

    function getPerson(uint256 index) public view override returns (Person memory) {
        return _people[index];
    }

}
```
Please note that in the files above and example code, inherited functions use `public` instead of `external`- and require using `virtual` which normal interfaces don't.


  [1]: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/b425a722409fdf4f3e0ec8f42d686f3c0522af19/contracts/governance/IGovernor.sol
  [2]: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/b425a722409fdf4f3e0ec8f42d686f3c0522af19/contracts/governance/compatibility/IGovernorCompatibilityBravo.sol