Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript generic contraints with extending and default parameters

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?

like image 849
Dirk-Jan Avatar asked Mar 14 '18 20:03

Dirk-Jan


People also ask

How do you pass a generic class as parameter 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.

What TypeScript keyword is used to apply a constraint to a generic type parameter?

Use extends keyword to constrain the type parameter to a specific type.

What is the correct way to define a generic class in TypeScript?

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> .

Does TypeScript support generic interface?

In TypeScript, you can create generic functions, generic methods, generic interfaces, and generic classes.


1 Answers

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.

like image 188
Behrooz Avatar answered Sep 24 '22 03:09

Behrooz