If you're dealing with known fields from a generic type, the way to allow wildcards is by using T & {[key: string]: unknown}
. Any fields that are known must comply with the type's constraints, while other fields are allowed (and considered of type unknown
).
Here's an example:
type WithWildcards<T> = T & { [key: string]: unknown };
function test(foo: WithWildcards<{baz: number}>) {}
test({ baz: 1 }); // works
test({ baz: 1, other: 4 }); // works
test({ baz: '', other: 4 }); // fails since 'baz' isn't a number
Then, if you have a generic type T
, you can allow wildcard fields with WithWildCards<T>
.
Note that extra properties aren't flagged as errors if the object comes from anything other than an object literal. TypeScript just informs you that adding those properties in the literal is unnecessary.
Check out some other scenarios where extra properties are and aren't permitted
interface Foos{
a?: string
b?: string
}
type WithWildcards<T> = T & { [key: string]: unknown };
declare function acceptFoos(foo: Foos): void;
declare function acceptWild(foo: WithWildcards<Foos>):void
acceptFoos( {a:'', other: ''}) // ERROR as the property is extraneous
const data = {a:'', other: ''}
acceptFoos( data) // allowed as it's compatible; TypeScript doesn't require removing properties
const notCompat = {other: ''}
acceptFoos(notCompat) //ERROR: Type '{ other: string; }' has no properties in common with type 'Foos'
acceptFoos({a:'', ...notCompat}) // this is also allowed
acceptWild(notCompat) // allowed