Excess Property Checking for Object Literal
TypeScript uses structural typing (to work with typical JavaScript code).
function print_height(person: { height: number }): void {
console.log(person.height)
}
const somebody = { height: 150, weight: 150 };
print_height(somebody);
However, TypeScript has excess property checking for object literal.Thus if we inline the somebody
declaration above, TypeScript raises an error:
print_height({ height: 150, weight: 150 }); // Error!
However, partial overlap is still permitted:
type Point = {
x: number;
y: number;
}
type Label = {
name: string;
}
const partialOverlap: Point | Label = {
x: 0,
y: 0,
name: "origin"
}
function print_pl(pl: Point | Label) {
console.log(pl)
}
print_pl({ x: 0, y: 0, name: "origin"})
But overlap with conflicting properties is not permitted:
type Point = {
x: number;
y: number;
}
type SPoint = {
x: string;
y: string;
}
const conflictingProperties: Point | SPoint = {x: 0, y: "0"}
Function Overloads
Function type declaration supports overloading.
function plus(x: number, y: number): number;
function plus(x: string, y: string): string;
function plus(x: number, y: number): boolean; // see notes below
function plus(x: number | string, y: number | string): number | string | boolean { // this line is not overload
if (typeof x == "number" && typeof y == "number") {
return x + y;
} else if (typeof x == "string" && typeof y == "string") {
return `${x}${y}`; // equivalent to `x + y`.
}
}
In the above example, the third overload will not be matched forever.Because TypeScripts looks at the overload list,proceeding with the first overload attempts to call the function with the provided parameters.If it finds a match, it picks this overload as the correct overload.And since intersection type of functions are defined as function overloads in TypeScript,this breaks the commutative rule of intersection function.Given two function types F
and G
, F & G
and G & F
are not equivalent.
Not surprisingly, it is difficult to check function overloads for type equality.
With TypeScript’s conditional types, testing type equality is intuitive:
type EqEq<T, S> = [T] extends [S] ? ([S] extends [T] ? true : false) : false
But it cannot handle function overloads and function intersection:
type FunctionOverloadsEquality = EqEq<
{ (x: 0, y: null): void; (x: number, y: null): void },
{ (x: number, y: null): void; (x: 0, y: null): void }> // true
type F = (x: 0, y: null) => void
type G = (x: number, y: string) => void
type FunctionIntersectionEquality = EqEq<F & G, G & F> // true
To check function overloads’ type equality, use this cleaver implementation by Matt McCutchen:
type EqEqEq<X, Y> =
(<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? true : false;
type FunctionOverloads = EqEqEq<
{ (x: 0, y: null): void; (x: number, y: null): void },
{ (x: number, y: null): void; (x: 0, y: null): void }> // false
However, it still cannot handle function intersection:
type FunctionIntersection = EqEqEq<F & G, G & F> // true
Besides, EqEq
considers any
to equal to any type except never
.This makes sense, since unlike Any
or Anything
in other languages, TypeScript’s any
is not a top type.any
is assignable to any type except never
.EqEqEq
is more strict and does not consider any
to be identical to other type.
Literal String, Literal Number, But Unique Symbol
TypeScript has literal strings and literal numbers, but there is no literal symbols.Instead, there is unique symbols (only allowed in const declarations).
In other words:
let no_unique_string_type: 'non sense but valid' = 'non sense but valid'
let no_literal_symbol: symbol = Symbol("cannot express Symbol(0) | Symbol(1)")
Top comments (0)