OmitIndexSignature
Omit any index signatures from the given object type, leaving only explicitly defined properties.
This is the counterpart of PickIndexSignaturePickIndexSignature.
Use-cases: - Remove overly permissive signatures from third-party types.
This type was taken from this StackOverflow answer.
It relies on the fact that an empty object ({}{}) is assignable to an object with just an index signature, like Record<string, unknown>Record<string, unknown>, but not to an object with explicitly defined keys, like Record<'foo' | 'bar', unknown>Record<'foo' | 'bar', unknown>.
(The actual value type, unknownunknown, is irrelevant and could be any type. Only the key type matters.)
`const indexed: Record<string, unknown> = {}; // Allowed
// @ts-expect-error const keyed: Record<'foo', unknown> = {}; // Error // TS2739: Type '{}' is missing the following properties from type 'Record<"foo" | "bar", unknown>': foo, bar ` Instead of causing a type error like the above, you can also use a conditional type to test whether a type is assignable to another:
type Indexed = {} extends Record<string, unknown> ? '✅ type Indexed = {} extends Record<string, unknown> ? '✅ {}is assignable tois assignable toRecord<string, unknown>' : '❌ ' : '❌ {}is NOT assignable tois NOT assignable toRecord<string, unknown>`';
type IndexedResult = Indexed;
//=> '✅ {}{} is assignable to Record<string, unknown>Record<string, unknown>'
type Keyed = {} extends Record<'foo' | 'bar', unknown>
? '✅ {}{} is assignable to Record<\'foo\' | \'bar\', unknown>Record<\'foo\' | \'bar\', unknown>'
: '❌ {}{} is NOT assignable to Record<\'foo\' | \'bar\', unknown>Record<\'foo\' | \'bar\', unknown>';
type KeyedResult = Keyed;
//=> '❌ {}{} is NOT assignable to Record<\'foo\' | \'bar\', unknown>Record<\'foo\' | \'bar\', unknown>'
Using a [mapped type](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html#further-exploration), you can then check for eachUsing a [mapped type](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html#further-exploration), you can then check for eachKeyTypeofofObjectType`...
type OmitIndexSignature<ObjectType> = { [KeyType in keyof ObjectType // Map each key of type OmitIndexSignature<ObjectType> = { [KeyType in keyof ObjectType // Map each key of ObjectType... ]: ObjectType[KeyType]; // ...to its original value, i.e. ... ]: ObjectType[KeyType]; // ...to its original value, i.e. OmitIndexSignature == Foo. }; . };
...whether an empty object ({}{}) would be assignable to an object with that KeyTypeKeyType (Record<KeyType, unknown>Record<KeyType, unknown>)...
type OmitIndexSignature<ObjectType> = { [KeyType in keyof ObjectType // Is type OmitIndexSignature<ObjectType> = { [KeyType in keyof ObjectType // Is {}assignable toassignable toRecord<KeyType, unknown>? as {} extends Record<KeyType, unknown> ? never // ✅ ? as {} extends Record<KeyType, unknown> ? never // ✅ {}is assignable tois assignable toRecord<KeyType, unknown> : KeyType // ❌ : KeyType // ❌{}is NOT assignable tois NOT assignable toRecord<KeyType, unknown> ]: ObjectType[KeyType]; }; ]: ObjectType[KeyType]; };
If {}{} is assignable, it means that KeyTypeKeyType is an index signature and we want to remove it. If it is not assignable, KeyTypeKeyType is a "real" key and we want to keep it.
See also:PickIndexSignature Object
type OmitIndexSignature<ObjectType> = {
[KeyType in keyof ObjectType as {} extends Record<
KeyType,
unknown
>
? never
: KeyType]: ObjectType[KeyType];
};type OmitIndexSignature<ObjectType> = {
[KeyType in keyof ObjectType as {} extends Record<
KeyType,
unknown
>
? never
: KeyType]: ObjectType[KeyType];
};ObjectType
ObjectTypeObjectType