I am very confusing about this, why 1 extends {[Key: string]: never}
is false. For example:
type T = 1 extends {[Key: string]: never} ? true : false;
Why T returns false.
CodePudding user response:
A type of the form {[key: string]: XXX}
has a string
index signature. A value of such a type may have any number (including zero) of string-keyed members (members are properties, which include methods, and include inherited properties); and any string-keyed members that it does have must be of type XXX
:
let dict: { [key: string]: string };
dict = { a: "hello", b: "goodbye" }; // okay
dict = { a: "hello", b: 123 }; // error!
// ----------------> ~
// Type 'number' is not assignable to type 'string'.
dict = {} // okay
The never
type is TypeScript's bottom type; there are no values of type never
. It is to types what the empty set is to sets. No matter what value you have, it will not be of type never
. You should not be able to observe a value of type never
.
Putting those together, a value of type {[key: string]: never}
may have any number of string-keyed members (including zero), and any string-keyed members that it does have must be of type never
. But since no value is of type never
, it means that a value of type {[key: string]: never}
must have exactly zero string-keyed members:
let neverDict: { [key: string]: never };
neverDict = { a: "hello", b: "goodbye" }; // error!
// ---------> ~ -------> ~
// Type 'string' is not assignable to type 'never'.
dict = {} // okay
Or at least any string-keyed members that are present must be expected to be unobservable:
function error(): never { throw new Error(); }
function special() {
dict = { a: error() } // okay
}
Here we assign a value of type {a: never}
to dict
, which is fine, but note that if you ever tried to run the program to look at such a value it would be unobservable because an error will be thrown.
So, in order to be assignable to {[key: string]: never}
, a type must have no string-keyed members.
On the other hand we have the type 1
. This is a numeric literal type which is a subtype of number
. Every value of type 1
is also a value of type number
. Furthermore, number
is a subtype of Number
, the interface for the wrapper object that number
values are given when you index into them like objects.
Here's the definition of the Number
interface from the TypeScript standard library:
interface Number {
toString(radix?: number): string;
toFixed(fractionDigits?: number): string;
toExponential(fractionDigits?: number): string;
toPrecision(precision?: number): string;
valueOf(): number;
}
Do note that all primitives in JavaScript except null
and undefined
have such wrapper objects, and so these primitives can be assigned to interfaces and are thus "object-like" in their treatment by TypeScript.
So, 1
is a subtype of number
, which is a subtype of Number
, which has some known members as shown above:
let one: 1 = 1;
let prim: number = 1;
let intf: Number = prim;
(1).toFixed // (method) Number.toFixed(fractionDigits?: number | undefined): string
And so now we can see that 1
should not be assignable to {[key: string]: never}
. The former has several members with keys like "toFixed"
and "toExponential"
, and these members have values of function types. Functions are actual values which are not assignable to never
. So this is an error:
neverDict = one; // error!
// Type 'number' is not assignable to type '{ [key: string]: never; }'.
And therefore 1 extends {[key: string]: never}
is false, and so the conditional type you wrote takes the false branch:
type T = 1 extends { [key: string]: never } ? true : false;
// type T = false