So I am trying to write a function which has a generic which extends a certain object thus constrains it. Next I would like to use this generic together with a definition of a parameter to generate a new "enhanced" parameter. This is all good but as soon as I want to introduce a default value to the parameter TypeScript complains with a message as follow (Some different variations of this in the playground):
Function:
const test1 = <T extends { foo?: string }>(options: T & { bar?: boolean } = {foo:
''}) => {
console.log(options);
}
The error:
Type '{ foo: string; }' is not assignable to type 'T & { bar?: boolean; }'. Object literal may only specify known properties, but 'foo' does not exist in type 'T & { bar?: boolean; }'. Did you mean to write 'foo'?
The compiler warns me that I probably wanted to use foo, which I actually did. Is it simply not possible to use a generic in this way or is this a bug in TypeScript?
Assigning Generic ParametersBy passing in the type with the <number> code, you are explicitly letting TypeScript know that you want the generic type parameter T of the identity function to be of type number . This will enforce the number type as the argument and the return value.
Use extends keyword to constrain the type parameter to a specific type.
TypeScript supports generic classes. The generic type parameter is specified in angle brackets after the name of the class. A generic class can have generic fields (member variables) or methods. In the above example, we created a generic class named KeyValuePair with a type variable in the angle brackets <T, U> .
In TypeScript, you can create generic functions, generic methods, generic interfaces, and generic classes.
Type T
at the definition time is unknown, so the compiler throws this error that you cannot initialize something you are unaware of. There are a couple of workarounds I can think of, but not sure how useful they are for your case.
You can leave type T
as is, and use union types for the options
parameter as follows:
const test1 = <T> (options: T | { bar?: boolean } | { foo?: string } = { foo: '' }) => {
console.log(options);
};
Another workaround is to use a type assertion and manually tell the compiler that the initializer is of the type it needs to be:
const test2 = <T extends { foo?: string }>(options: T & { bar?: boolean } = { foo: '' } as T & { bar?: boolean }) => {
console.log(options);
};
But keep in mind that these are just workarounds and whenever you have to use a workaround, it implies a flaw in the design. Maybe you can take a second look at your logic and improve it in order to remove the need for these workarounds. Or maybe, you can skip argument initialization and add options.foo = options.foo || '';
as the first line of code in your function. Just some ideas.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With