I’m encountering an issue where TypeScript doesn’t catch a type mismatch when using an index signature type with a more specific type where some keys are optional but with a different type. Here’s a simplified example:
type Filter = {
[key: string]: string;
};
type SpecificFilter = {
// If not made optional, we are fine, since
// TS won’t let us assign a `Filter` to a
// `SpecificFilter`:
// Property 'name' is missing in type 'Filter'
name?: string[];
};
const process = (filter: SpecificFilter) => {
return filter.name?.map((name) => name.toLowerCase());
};
const play = (filter: Filter) => {
// TypeScript doesn't complain, but `filter` isn't a valid `SpecificFilter`
process(filter);
};
// This will throw at runtime because `name` is not an array
play({ name: "John" });
TypeScript doesn’t raise an error when passing a Filter
to a function expecting a SpecificFilter
, even though the types are incompatible (string
vs string[]
).
Is there a TypeScript configuration or a coding pattern to catch this kind of type mismatch?
2
This is unfortunately a longstanding known bug in TypeScript, as reported in microsoft/TypeScript#27144. Currently index signatures are always considered assignable to weak types (object types whose properties are all optional), even if the value type for the index signature is incompatible with the value type for the optional properties.
The bug is on the Backlog, meaning that anyone from the community is welcome to submit a pull request to address it. So anyone who really wants to see this bug fixed should consider trying to fix it themself. Until and unless there’s a fix, you’ll just need to avoid or work around it.
One possible workaround would require that you add optional properties to the index signature for any key you expect to conflict:
type Filter = { [key: string]: string | undefined, name?: string };
(Note that I had to add undefined
to the index signature type for that to be a valid type without enabling the --exactOptionalPropertyTypes
compiler option.) Now you get the expected error:
const play = (filter: Filter) => {
process(filter); // error! Type 'string | undefined'
// is not assignable to type 'string[] | undefined'.
};
Yes, it’s not great, since obviously this can’t catch optional keys you don’t know about in advance. But there isn’t much to be done here, unfortunately.
Playground link to code