[Typescript] Be Specific for Better Inference
This exercise begins with a makeStatus
function that takes in an array of TStatuses
that extends an array of strings. The statuses are then returned, kind of like an identity function:
const makeStatus = <TStatuses extends string[]>(statuses: TStatuses) => {
return statuses;
};
Looking at the tests, we can see that we're expecting statuses
to be an array of INFO
or DEBUG
or ERROR
or WARNING
, all of the things that we pass in here.
const statuses = makeStatus(["INFO", "DEBUG", "ERROR", "WARNING"]);
// ^? string[]
type tests = [
Expect<Equal<typeof statuses, Array<"INFO" | "DEBUG" | "ERROR" | "WARNING">>>, // not working
];
However that's not what we're getting-- instead, we just get an array of strings.
So idea is to get actual value of each string array memeber.
In Typescript compiler, if you use generic on array level, then it infer typeof this array
["INFO", "DEBUG", "ERROR", "WARNING"] // string[]
["INFO", "DEBUG", "ERROR", 1] // (string | number)[]
[1,2,3,4] // number[]
[1,2,3,undefined] //(number | undefined)[]
So, if you want to get so array member as type, you have to go deeper, which means, the generic type cannot be array level any more, it needs to be array member level.
const makeStatus = <TStatus extends string>(statuses: TStatus[]) => {
return statuses;
};
So we got correct results.
const statuses = makeStatus(['INFO', 'DEBUG', 'ERROR', 'WARNING']);
// ^? ("INFO" | "DEBUG" | "ERROR" | "WARNING")[]
type tests = [
Expect<Equal<typeof statuses, Array<'INFO' | 'DEBUG' | 'ERROR' | 'WARNING'>>>
];
If we change the array to be (number | string)[]
const makeStatus = <TStatus extends string | number>(statuses: TStatus[]) => {
return statuses;
};
const statuses = makeStatus([1, 'DEBUG', 'ERROR', 'WARNING']);
// ^? (1 | "DEBUG" | "ERROR" | "WARNING")[]
type tests = [
Expect<Equal<typeof statuses, Array<1 | 'DEBUG' | 'ERROR' | 'WARNING'>>>
];