I'm writing a React component that wraps react-select's Select component, which has a prop declaration that starts like this:
export type SelectComponentsProps = { [key in string]: any };
export interface Props<OptionType extends OptionTypeBase = { label: string; value: string }> extends SelectComponentsProps {
I imported this type and had tried to declare the props of my wrapper component like this:
import { Props as SelectProps } from "react-select/src/Select";
type Props = Omit<SelectProps, "inputId"> & { ... some other stuff };
But I had a problem where I could "type-safely" pass anything I wanted to my component, even for fields that had explicit type declarations on SelectProps
:
// okay
<MySelectWrapper onChange="definitely not a function"/>
// not okay, fails type checking
<Select onChange="definitely not a function"/>
After some digging, I found that the combination of Omit
and an index signature (in the react-select example: SelectComponentsProps
) causes the compiler to drop explicitly-specified fields and instead just use the index signature.
interface ArbitraryKeyable {
foo: number;
[key: string]: any;
}
const value = {
foo: 'not a number',
bar: 'this can be anything'
}
// Fails: foo is the wrong type.
const arbitrary: ArbitraryKeyable = value;
type OmittedArbitraryKeyable = Omit<ArbitraryKeyable, 'this is not a field'>;
// Succeeds, even though the type should functionally be the same.
const omittedArbitrary: OmittedArbitraryKeyable = value;
Here's what the playground thinks that type is:
So of course it accepts everything! Is there a way to define a version of Omit
that preserves the explicitly-defined fields? Alternately, is there a type operation I can perform to remove the index signature and only have the explicit fields? (I don't really need it for my use-case, so I'd be okay losing that flexibility to gain type safety elsewhere.)