There was this challenge
Implement a generic First that takes an Array T and returns its first element's type.
type arr1 = ["a", "b", "c"];
type arr2 = [3, 2, 1];
type head1 = First<arr1>; // expected to be 'a'
type head2 = First<arr2>; // expected to be 3
But I am confused. Since the tasks says we should return first elements type, shouldn't return in first case be string
? and in second number
?
What am I missing?
Ps. This is the given solution and it indeed returns "a" instead of string
type First<T extends any[]> = T extends [] ? never : T[0];
Update:
In below code head1
is inferred as string
.
let arr1 = ["a", "b", "c"]; // use let instead of type
type head1 = First<typeof arr1>;
Why when I used type array1
as in my question, head1
was inferred as "a" and above as "string"?
CodePudding user response:
The key is that arr1
and arr2
aren't array types; they're tuple types. In JavaScript, tuples are simply arrays, but in TypeScript's type system every element of a tuple has its own type.
This is easier to see if you use "actual" types instead of literals, for example:
type arr1 = [string, number, Object];
const arr1Value: arr1 = ["hello", 42, {}];
type head1 = First<arr1>; // string
Note that arr1
is not an array. It's a type of array (or tuple), and arr1Value
is an array of that type.
If you don't provide the type of an array literal, it is inferred:
let arr1 = ["a", "b", "c"]; // arr1 is of type string[]
Now, the inferred type of arr1
could have been inferred as ['a', 'b', 'c']
; that is, a tuple of those three strings. But that's almost never what you want: for example, it wouldn't let you push more values onto the array. So TypeScript infers that it's a string[]
instead. (If you do want to infer a tuple type, add as const
at the end.)
CodePudding user response:
Type in TypeScript doesn't necessarily have to be string
. It is a valid type, however very general.
You can limit the possible strings to say just 'a'
, 'b'
or 'c'
using:
type Abc = 'a' | 'b' | 'c';
If you now create a variable with this type declared, you will get compilation error whenever incorrect value is defined:
const name: Abc = 'John'; // TypeScript will mark this as incorrect
Now, to provide you with the solution, apparently this is enough:
type First<T extends any[]> = T[0];