any
、unknown
、never
、void
。TypeScript 具有定义函数参数和返回值的特定语法。
function getTime(): number { return new Date().getTime(); } let time = getTime(); // let time: number console.log(time);
如果没有定义返回类型,TypeScript 将尝试通过返回的变量或表达式的类型来推断它。
void
可用于指示函数不返回任何值。function printHello(): void { console.log('Hello!'); }
function multiply(a: number, b: number) { return a * b; }
如果没有定义参数类型,TypeScript 将默认使用 any,除非额外的类型信息可用,如默认参数和类型别名。
// 这里的 `?` 运算符将参数 `c` 标记为可选 function add(a: number, b: number, c?: number) { return a + b + (c || 0); } console.log(add(2,5));
function pow(value: number, exponent: number = 10) { return value ** exponent; }
TypeScript 还可以从默认值推断类型。
function pow(value, exponent = 10) { return value ** exponent; } console.log(pow(10, '2')); // Argument of type 'string' is not assignable to parameter of type 'number'.
function divide({ dividend, divisor }: { dividend: number, divisor: number }) { return dividend / divisor; } console.log(divide({dividend: 10, divisor: 2}));
function add(a: number, b: number, ...rest: number[]) { return a + b + rest.reduce((p, c) => p + c, 0); } console.log(add(10,10,10,10,10));
type Negate = (value: number) => number; // 参数 value 自动从 Negate 类型被分配 number 类型 const negateFunction: Negate = (value) => value * -1; console.log(negateFunction(10));
function makeDate(timestamp: number): Date; function makeDate(m: number, d: number, y: number): Date; function makeDate(mOrTimestamp: number, d?: number, y?: number): Date { if (d !== undefined && y !== undefined) { return new Date(y, mOrTimestamp, d); } else { return new Date(mOrTimestamp); } } const d1 = makeDate(12345678); const d2 = makeDate(5, 5, 5); const d3 = makeDate(1, 3); // No overload expects 2 arguments, but overloads do exist that expect either 1 or 3 arguments.
在本例中,我们编写了两个重载:一个接受一个参数,另一个接受三个参数。前两个签名称为重载签名,但它们都不能用两个参数调用。
在下面这个示例中,我们可以用字符串或数组调用它。但是,我们不能使用可能是字符串或数组的值调用它,因为 TypeScript 只能将函数调用解析为单个重载:
function len(s: string): number; function len(arr: any[]): number; function len(x: any[] | string) { return x.length; } len(""); // OK len([0]); // OK len(Math.random() > 0.5 ? "hello" : [0]); // No overload matches this call.
因为两个重载都有相同的参数计数和相同的返回类型,所以我们可以编写一个非重载版本的函数:
function len(x: any[] | string) { return x.length; }
现在我们可以使用任意一种值调用它,所以如果可能,首选具有联合类型的参数,而不是重载。
枚举是一个特殊的“类”,表示一组常量(不可更改的变量)。使用枚举类型可以为一组数值赋予更加友好的名字。枚举有两种数据类型:string
和 numer
。
0
,后面的值依次值加 1
。enum CardinalDirections { North, East, South, West }; let currentDirection: CardinalDirections = CardinalDirections.North; console.log(currentDirection); // '0' 因为 North 是第一个值 // currentDirection = 'North'; // Error: "North" is not assignable to type 'CardinalDirections'.
enum CardinalDirections { North = 1, East, South, West } console.log(CardinalDirections.North); // logs 1 console.log(CardinalDirections.West); // logs 4
enum StatusCodes { NotFound = 404, Success = 200, Accepted = 202, BadRequest = 400 }; console.log(StatusCodes.NotFound); // logs 404 console.log(StatusCodes.Success); // logs 200
string
类型比 numer
类型枚举更常见,因为它们的可读性和目的性更强。enum CardinalDirections { North = 'North', East = "East", South = "South", West = "West" }; console.log(CardinalDirections.North); // logs "North" console.log(CardinalDirections.West); // logs "West"
可以混合字符串和数字枚举值,但不建议这样做。
enum StatusCodes { NotFound = 404, Success = 200, Accepted = 202, BadRequest = 400 }; let s1 = StatusCodes[200]; // string | undefined console.log(s1); // Success
const value = 0; enum List { A = value, B = 2, // 必须初始化 C, }
联合类型(Union Types)可以通过 |
运算符将变量设置多种类型,赋值时可以根据设置的类型来赋值。当一个值可以是多个单一类型时,可以使用联合类型。例如当变量是 string
或 number
时。
function printStatusCode(code: string | number) { console.log(`My status code is $[code].`) } printStatusCode(404); printStatusCode('404');
注意:使用联合类型时,需要知道你的类型是什么,以避免类型错误:
function printStatusCode(code: string | number) { console.log(`My status code is ${code.toUpperCase()}.`); // error: Property 'toUpperCase' does not exist on type 'string | number'. Property 'toUpperCase' does not exist on type 'number' }
在上述示例中,因为 toUpperCase()
是一个字符串方法,而数字无法访问它。
TypeScript 允许类型与使用它们的变量分开定义。类型别名和接口允许在不同的变量之间轻松共享类型。
type Point = { x: number; y: number; }; function printCoord(pt: Point) { console.log("The coordinate's x value is " + pt.x); console.log("The coordinate's y value is " + pt.y); } printCoord({ x: 100, y: 100 });
type ID = number | string;
type Direction = 'center' | 'left' | 'right'; let d: Direction = ''; // Type '""' is not assignable to type 'Direction'.
type BooleanString = `${boolean}`; const bool: BooleanString = '1'; // Type '"1"' is not assignable to type '"false" | "true"'. type SerialNumber= `${number}.${number}`; const id: SerialNumber= '1.2';
接口类似于类型别名,但是只适用于对象类型。
printCoord
的值的结构——它是否具有预期的属性。只关心类型的结构和功能,这就是我们将 TypeScript 称为结构类型系统的原因。interface Point { x: number; y: number; } function printCoord(pt: Point) { console.log("The coordinate's x value is " + pt.x); console.log("The coordinate's y value is " + pt.y); } printCoord({ x: 100, y: 100 });
type Window = { title: string } // Error: Duplicate identifier 'Window'. type Window = { ts: TypeScriptAPI } interface Window { title: string } interface Window { ts: TypeScriptAPI }
extends
关键字可以继承另一个接口、类、类型别名来扩展成员,支持多继承,在 extends
关键字之后用逗号分隔。interface Show { isShow: boolean; } type Graphic = { name: string; } class Point { x: number; y: number; } interface Point3d extends Point, Graphic, Show { z: number; } const point3d: Point3d = { x: 1, y: 2, z: 3, name: '1', isShow: true };
interface i1 { [index: number]: string } let list: i1 = ["0", "1", "2"]; // list2 = ["0", 1, "2"] // Type 'number' is not assignable to type 'string'. interface i2 { [index: string]: number } const list2: i2 = {}; list2["0"] = 0; list2[1] = "1"; // Type 'string' is not assignable to type 'number'.
接口允许我们通过扩展其他类型来构建新类型。TypeScript 还提供了另一种称为交叉类型的结构,使用 &
运算符定义,主要用于组合现有的对象类型。
interface Colorful { color: string; } interface Circle { radius: number; } function draw(circle: Colorful & Circle) { console.log(`Color was ${circle.color}`); console.log(`Radius was ${circle.radius}`); } draw({ color: "blue", radius: 42 }); // 'raidus' does not exist in type 'Colorful & Circle'. Did you mean to write 'radius'? draw({ color: "red", raidus: 42 });
在这里,我们将 Colorful
和 Circle
相交以生成一个包含 Colorful
和 Circle
的所有成员的新类型。
interface A { name: string; age: number; } interface B { name: string; height: string; } type Person = A & B; // 相当于求并集 const person: Person = { name: 'Tom', age: 18, height: '60kg' };
interface Animal { name: string } type Person = Animal & { age: number; }
type Animal = { name: string } type Bear = Animal & { honey: boolean }
never
,因为任何类型都不能满足同时属于多种原始类型。type Useless = string & number; // type Useless: never Useless = 1; // 'Useless' only refers to a type, but is being used as a value here.
TypeScript 向 JavaScript 类添加了类型和可见性修饰符。
class Person { name: string; } const person = new Person(); person.name = "Jane";
public
-(默认)允许从任何地方访问类成员private
- 只允许从类内部访问类成员protected
- 允许从自身和继承它的任何类访问类成员class Person { private name: string; constructor(name: string) { this.name = name; } getName(): string { return this.name; } } const person = new Person("Jane"); console.log(person.getName()); // person.name isn't accessible from outside the class since it's private
class Person { constructor(private name: string) {} getName(): string { return this.name; } } const person = new Person("Jane"); console.log(person.getName()); // Jane
readonly
关键字可以防止类成员被更改,只读属性必须在声明时或构造函数里被初始化,readonly
关键字也可以在构造函数中定义类成员。class Person { readonly name: string = 'Jane'; constructor(name?: string) { if(name) this.name = name; } } const person = new Person("a"); // person.name = ''; // Cannot assign to 'name' because it is a read-only property.
extends
关键字继承另一个类,一个类只能继承一个类;通过 implements
关键字实现接口,一个类支持实现多个接口,在 implements
关键字之后用逗号分隔。interface Shape { getArea: () => number; } class Rectangle implements Shape { constructor(protected readonly width: number, protected readonly height: number) {} getArea(): number { return this.width * this.height; } } class Square extends Rectangle { constructor(width: number) { super(width, width); } }
override
关键字显式标记,它可以帮助防止意外重写不存在的方法。使用设置 noImplicitOverride
可以强制在重写时使用它。class Rectangle { constructor(protected readonly width: number, protected readonly height: number) {} toString(): string { return `Rectangle[width=${this.width}, height=${this.height}]`; } } class Square extends Rectangle { constructor(width: number) { super(width, width); } override toString(): string { return `Square[width=${this.width}]`; } }
abstract
关键字定义抽象类,未实现的成员也需要使用 abstract
关键字标识。抽象类不能直接实例化,因为它们没有实现其所有成员。abstract class Polygon { abstract getArea(): number; toString(): string { return `Polygon[area=${this.getArea()}]`; } } class Rectangle extends Polygon { constructor(protected readonly width: number, protected readonly height: number) { super(); } getArea(): number { return this.width * this.height; } }
getters/setters
来截取对对象成员的访问,有效地控制对对象成员的访问。只带有 get
不带有 set
的存取器自动被推断为 readonly。class Employee { login: boolean; private _fullName: string; get fullName(): string { return this._fullName; } set fullName(newName: string) { console.log(this.login); if (this.login === true) { this._fullName = newName; } else { console.log("Error: Unauthorized update of employee!"); } } } const employee = new Employee(); employee.login = true; employee.fullName = "Bob Smith"; if (employee.fullName) { console.log(employee.fullName); }
class StaticMem { static num: number; static disp(): void { console.log("num 值为 " + StaticMem.num); } } StaticMem.num = 12; StaticMem.disp();