I am new to TS and am learning it by reading Programming Typescript book by O Reilly. In the book, the author is implementing his own version of JS's built-in call function.
function call<T extends unknown[], R>(
f: (...args: T) => R,
...args: T
): R {
return f(...args)
}
function fill(length: number, value: string): string[] {
return Array.from({length}, () => value)
}
call(fill, 10, 'a') // evaluates to an array of 10 'a's
One thing I am really struggling with is why is unknown used here? Why can't we do it like this?
function call<T, R>(
f: (...args: T[]) => R,
...args: T[]
): R {
return f(...args)
}
Of course, this version errors out but why?
Thanks
CodePudding user response:
Because we might have better information.
function call<T, R>(
f: (...args: T[]) => R,
...args: T[]
): R
This demands that we have some type T for which the array takes zero or more T (in the form of an array) as arguments. If we have a function that takes a variable number of strings, that works great.
However, there are other types that extend unknown[] that aren't, in as many words, arrays. Namely, those are tuple types. If we want to call this with a function
function foobar(x: string, y: number)
Then your version of the function would require that T be string | number, which means I could call your function with [0, "A"], or [0, 0, 0, 0, 0], or [], or any number of other incorrect call signatures. However, the other function you proposed,
function call<T extends unknown[], R>(
f: (...args: T) => R,
...args: T
): R {
return f(...args)
}
This will take T to be [string, number]. Every instance of [string, number] can be assigned to a variable of type unknown[], so the former is compatible with the latter. And now we can only call this function with a list whose size is known statically to be 2 and whose first element is a string and the second is a number.
CodePudding user response:
By writing T extends unknown[] you restrain T to have all the properties of an unknown[]. Two consequences:
- The caller of the
callfunction can't use it with aTthat wouldn't satisfy this constraint - In the implementation, you can use a variable of type
Tlike anunknown[]even if the actual type is not known (only the caller knows it)
