简体   繁体   中英

How to define a generic function type in Typescript; two similar ways?

I'm trying to define some generic function types in Typescript. It seems like there are two similar ways to do it, and they both kind-of work. However the second form - see below ConcatY - doesn't seem as flexible, or at least I don't know how to indicate that a function takes one of these with specific argument types. Is there a way to define a function that takes a ConcatY for numbers? In general, how should I think about the differences between these two ways to define a generic function?

// Two similar looking function types
type ConcatX<T> = (a: T, b: T) => T;
type ConcatY = <T>(a: T, b: T) => T;

// Can create instances of each of these types
const sum: ConcatX<number> = (a, b) => a + b;
const product: ConcatY = (a: number, b: number) => a + b;

// Can define a function that takes a ConcatX
function DoMathX(sum: ConcatX<number>) {
  console.log(`1 + 1 is ${sum(1, 1)}`);
}

// But can't define a function that takes a ConcatY for numbers
// A and B are "unknown"
// Or is there a way?
function DoMathTwo(sum: ConcatY) {}

This:

type ConcatX<T> = (a: T, b: T) => T;

Is a generic type alias , which contains a function that uses the generic parameter. The generic parameter this function uses is locked in once the type is resolved. That can be handy when some type needs to set the type of your function.

For instance here:

const sum: ConcatX<number> = (a, b) => a + b;

This says that, externally to this function, you declare that the arguments of this function are numbers.

Note that this is not really part of the function type at all. This approach isn't fundamentally different than something like:

type ContatX<T> = { sum(a: T, b: T): T, someValue: T }

The point being that the T is set outside the function entirely, and the function just picks that up to use.


This:

type ConcatY = <T>(a: T, b: T) => T;

Is a generic function . The generic parameter is set when the function is called , and can be different each time it's called. And that parameter may be inferred from the type of the arguments a and b .

You cannot lock down T ahead of time here because T is decided when the function is called and no sooner .

Which means there is no subtype of ConcatY that only takes numbers. To type guard against bad usage here you would instead check the return type:

const resultStr: string = sum(1,2) // error: cannot assign number to string
const resultNum: num = sum('a','b') // error: cannot assign string to number

In both cases the function call is completely valid, but the result is not what the type system is expecting, so you get an error.


If you want a specific subtype of concat where the arguments must be numbers, then you want the generic type ConcatX .

But if your function could take lots of different arguments, and the return type depends on the type of those arguments, and you don't know exactly what types it will be called with ahead of time, then you want the generic function ContactY .

Which all means that if you want to:

"to indicate that a function takes one of these with specific argument types"

Then you need to use ContactX with a generic parameter to create a function type with the T locked to whatever you want.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM