function类型
方法作为参数
- 使用函数类型表达式:
(a: string) => void - 使用 type:
type GreetFunction = (a: string) =>void;
调用签名
// 声明一个方法的 type
type DescribableFunction = {
description: string;
// 注意:在参数列表和返回类型之间使用 : 而不是 =>
(someArg: number): boolean;
};
// 定义 doSomething,接收一个方法作为参数,类型是上面定义的 type
function doSomething(fn: DescribableFunction) {
console.log(fn.description + " returned " + fn(6));
}
// 如果想要传入 doSomething 中,入参和返回值类型必须与上面定义的 type 对应上
function myFunc(someArg: number) {
return someArg > 3;
}
myFunc.description = "default description";
doSomething(myFunc);
构造签名
type SomeConstructor = {
new (s: string): SomeObject;
};
function fn(ctor: SomeConstructor) {
return new ctor("hello");
}
// 组合方法
interface CallOrConstruct {
(n?: number): string;
new (s: string): Date;
}
泛型函数
function firstElement<T>(arr: T[]): T | undefined {
return arr[0];
}
// s is of type 'string'
const s = firstElement(["a", "b", "c"]);
// n is of type 'number'
const n = firstElement([1, 2, 3]);
// u is of type undefined
const u = firstElement([]);
推断
使用多个
function map<Input, Output>(arr: Input[], func: (arg: Input) => Output): Output[] {
return arr.map(func);
}
// Parameter 'n' is of type 'string'
// 'parsed' is of type 'number[]'
const parsed = map(["1", "2", "3"], (n) => parseInt(n));
约束条件
function longest<Type extends { length: number }>(a: Type, b: Type) {
if (a.length >= b.length) {
return a;
} else {
return b;
}
}
// longerArray is of type 'number[]'
const longerArray = longest([1, 2], [1, 2, 3]);
// longerString is of type 'alice' | 'bob'
const longerString = longest("alice", "bob");
// 报错! Numbers don't have a 'length' property
const notOK = longest(10, 100);
使用约束值
function minimumLength<Type extends { length: number }>(
obj: Type,
minimum: number
): Type {
if (obj.length >= minimum) {
return obj;
} else {
return { length: minimum };
}
错误的,因为如果传入一个数组和一个数字,特殊情况下会有问题,比如:
// 'arr'方法 返回 { length: 6 }
const arr = minimumLength([1, 2, 3], 6);
// 报错,因为 对象 { length: 6 } 没有 slice 方法
console.log(arr.slice(0));
指定类型参数
function combine<Type>(arr1: Type[], arr2: Type[]): Type[] {
return arr1.concat(arr2);
}
// 如果这么使用就会出错
const arr = combine([1, 2, 3], ["hello"]);
// 修改:手动指定 type
const arr = combine<string | number>([1, 2, 3], ["hello"]);
可选参数(?)
function f(x?: number) {
// ...
}
f(); // OK
f(10); // OK
注意:尽管参数被指定为 number 类型,但 x 参数实际上将具有 number | undefined 类型
在函数中声明this
const user = {
id: 123,
admin: false,
becomeAdmin: function () {
this.admin = true;
},
};
interface DB {
filterUsers(filter: (this: User) => boolean): User[];
}
const db = getDB();
// 注意,需要使用 function 而不是箭头函数来获得此行为
const admins = db.filterUsers(function (this: User) {
return this.admin;
});
其他类型
void
不返回值的函数的返回值
object
- 指的是任何非基础值(string、number、bigint、boolean、symbol、null 或 undefined)
- 这与空对象类型
{ }不同 - 也与全局类型 Object 不同
unknown
- 代表任何值
- 这类似于 any 类型,但更安全,因为使用 unknown 值做任何事情都是不合法的
- 可以描述接受任何值而不在函数体中包含 any 值的函数
function safeParse(s: string): unknown {
return JSON.parse(s);
}
// Need to be careful with 'obj'!
const obj = safeParse(someRandomString);
never
- 从未观察到的值
- 在返回类型中,这意味着函数抛出异常或终止程序的执行
Function
全局类型 Function 描述了 bind、call、apply 等属性,以及 JavaScript 中所有函数值上的其他属性。它还具有 Function 类型的值始终可以被调用的特殊属性;这些调用返回 any:
function doSomething(f: Function) {
return f(1, 2, 3);
}
如果需要接受任意函数但不打算调用它,则类型 () => void 通常更安全
剩余形参和实参
剩余形参
给出的任何类型注释必须采用 Array<T> 或 T[] 形式,或者元组类型
function multiply(n: number, ...m: number[]) {
return m.map((x) => n * x);
}
// 'a' gets value [10, 20, 30, 40]
const a = multiply(10, 1, 2, 3, 4);
剩余实参
const args = [8, 5] as const;
// OK
const angle = Math.atan2(...args);
对象类型
属性修饰符
可选属性(?)
interface PaintOptions {
shape: Shape;
// 可有可无
xPos?: number;
yPos?: number;
}
readonly 属性
只读,不能改
interface SomeType {
readonly a: string;
}
注意:只是针对这个属性的,如果这个属性映射一个对象,对象里面的内容还是可以修改的
索引签名
不知道类型属性的所有名称,但确实知道值的类型的情况下,使用索引签名来描述可能值的类型
interface StringArray {
[index: number]: string;
}
const myArray: StringArray = getStringArray();
const secondItem = myArray[1];
索引签名属性只允许使用某些类型:string、number、symbol、模板字符串模式以及仅由这些组成的联合类型
interface NumberOrStringDictionary {
// 索引:返回值的类型
// 强制所有属性与其返回类型匹配
[index: string]: number | string;
length: number; // ok, length is a number
name: string; // ok, name is a string
}
// 防止修改
interface ReadonlyStringArray {
readonly [index: number]: string;
}
扩展类型
interface Colorful {
color: string;
}
interface Circle {
radius: number;
}
interface ColorfulCircle extends Colorful, Circle {}
const cc: ColorfulCircle = {
color: "red",
radius: 42,
};
交叉类型(&)
组合作用,必须同时拥有属性
interface Colorful {
color: string;
}
interface Circle {
radius: number;
}
type ColorfulCircle = Colorful & Circle;
类型操作
泛型
function identity<Type>(arg: Type): Type {
return arg;
}
let output = identity<string>("myString"); // 显示声明
let output = identity("myString"); // 类型推断
使用泛型类型变量
想使用 length 获取入参的长度
function loggingIdentity<Type>(arg: Type[]): Type[] {
console.log(arg.length);
return arg;
}
// 或者
function loggingIdentity<Type>(arg: Array<Type>): Array<Type> {
console.log(arg.length); // Array has a .length, so no more error
return arg;
}
泛型类型
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: <T>(arg: T) => T = identity;
// 或者写成这样
let myIdentity: { <T>(arg: T): T } = identity;
// 实际上等于 js 中的
function identity(arg) {
return arg;
}
let myIdentity = identity;
抽取转成接口:
interface GenericIdentityFn {
<Type>(arg: Type): Type;
}
function identity<Type>(arg: Type): Type {
return arg;
}
let myIdentity: GenericIdentityFn = identity;
// 优化
interface GenericIdentityFn<Type> {
(arg: Type): Type;
}
function identity<Type>(arg: Type): Type {
return arg;
}
let myIdentity: GenericIdentityFn<number> = identity;
泛型类
class GenericNumber<NumType> {
zeroValue: NumType;
add: (x: NumType, y: NumType) => NumType;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function (x, y) {
return x + y;
};
let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "";
stringNumeric.add = function (x, y) {
return x + y;
};
泛型约束
通过接口改写前面想要获取长度的例子
interface Lengthwise {
length: number;
}
function loggingIdentity<Type extends Lengthwise>(arg: Type): Type {
console.log(arg.length);
return arg;
}
// 报错,没有传入长度
loggingIdentity(3);
loggingIdentity({ length: 10, value: 3 });
泛型约束中使用类型参数
function getProperty<Type, Key extends keyof Type>(obj: Type, key: Key) {
return obj[key];
}
let x = { a: 1, b: 2, c: 3, d: 4 };
getProperty(x, "a");
getProperty(x, "m"); // 报错,x上不存在属性 m
在泛型中使用类类型
function create<Type>(c: { new (): Type }): Type {
return new c();
}
泛型参数默认值
declare function create<T extends HTMLElement = HTMLDivElement, U = T[]>(
element?: T,
children?: U
): Container<T, U>;
const div = create();
// const div: Container<HTMLDivElement, HTMLDivElement[]>
const p = create(new HTMLParagraphElement());
// const p: Container<HTMLParagraphElement, HTMLParagraphElement[]>
keyof类型运算符
keyof 操作符接受一个对象类型作为参数,并将其键生成一个字符串或数字字面量并集
type Point = { x: number; y: number };
type P = keyof Point; // "x" | "y"
// 如果该类型具有 string 或 number 索引签名,则 keyof 将返回这些类型:
type Arrayish = { [n: number]: unknown };
type A = keyof Arrayish; // number
type Mapish = { [k: string]: boolean };
type M = keyof Mapish; // string | number
typeof类型运算符
function f() {
return { x: 10, y: 3 };
}
type P = ReturnType<typeof f>;
// type P = {
// x: number;
// y: number;
// }
索引访问类型
type Person = { age: number; name: string; alive: boolean };
type Age = Person["age"]; // type Age = number
type I1 = Person["age" | "name"]; // type I1 = string | number
type I2 = Person[keyof Person]; // type I2 = string | number | boolean
type AliveOrName = "alive" | "name";
type I3 = Person[AliveOrName]; // type I3 = string | boolean
const MyArray = [
{ name: "Alice", age: 15 },
{ name: "Bob", age: 23 },
{ name: "Eve", age: 38 },
];
type Person = typeof MyArray[number];
// type Person = {
// name: string;
// age: number;
// }
type Age = typeof MyArray[number]["age"]; // type Age = number
// Or
type Age2 = Person["age"]; // type Age2 = number
// 重构
type key = "age";
type Age = Person[key];
条件类型
interface Animal {
live(): void;
}
interface Dog extends Animal {
woof(): void;
}
type Example1 = Dog extends Animal ? number : string; // type Example1 = number
type Example2 = RegExp extends Animal ? number : string; // type Example2 = string
映射类型
映射类型建立在索引签名的语法之上,用于声明未提前声明的属性类型:
type OnlyBoolsAndHorses = {
[key: string]: boolean | Horse;
};
const conforms: OnlyBoolsAndHorses = {
del: true,
rodney: false,
};
映射类型是一种泛型类型,它使用 PropertyKey 的联合来迭代键来创建类型:
type OptionsFlags<Type> = {
[Property in keyof Type]: boolean;
};
type Features = {
darkMode: () => void;
newUserProfile: () => void;
};
type FeatureOptions = OptionsFlags<Features>;
// type FeatureOptions = {
// darkMode: boolean;
// newUserProfile: boolean;
// }
映射修饰符
- 在映射期间可以应用两个额外的修饰符:
readonly和?分别影响可变性和可选性 - 可以通过添加前缀
-或+来移除或添加这些修饰符。如果不添加前缀,则假定为+
type CreateMutable<Type> = {
-readonly [Property in keyof Type]: Type[Property];
};
type LockedAccount = {
readonly id: string;
readonly name: string;
};
type UnlockedAccount = CreateMutable<LockedAccount>;
// type UnlockedAccount = {
// id: string;
// name: string;
// }
type Concrete<Type> = {
[Property in keyof Type]-?: Type[Property];
};
type MaybeUser = {
id: string;
name?: string;
age?: number;
};
type User = Concrete<MaybeUser>;
// type User = {
// id: string;
// name: string;
// age: number;
// }
通过 as 重新映射键
type MappedTypeWithNewProperties<Type> = {
[Properties in keyof Type as NewKeyType]: Type[Properties]
}
// 利用模版字符串
type Getters<Type> = {
[Property in keyof Type as `get${Capitalize<string & Property>}`]: () => Type[Property]
};
interface Person {
name: string;
age: number;
location: string;
}
type LazyPerson = Getters<Person>;
// type LazyPerson = {
// getName: () => string;
// getAge: () => number;
// getLocation: () => string;
// }
// 通过条件类型生成 never 来过滤掉键:
type RemoveKindField<Type> = {
[Property in keyof Type as Exclude<Property, "kind">]: Type[Property]
};
interface Circle {
kind: "circle";
radius: number;
}
type KindlessCircle = RemoveKindField<Circle>;
// type KindlessCircle = {
// radius: number;
// }
// 映射任意联合,不仅是 string | number | symbol 的联合,还可以映射任何类型的联合:
type EventConfig<Events extends { kind: string }> = {
[E in Events as E["kind"]]: (event: E) => void;
}
type SquareEvent = { kind: "square", x: number, y: number };
type CircleEvent = { kind: "circle", radius: number };
type Config = EventConfig<SquareEvent | CircleEvent>
// type Config = {
// square: (event: SquareEvent) => void;
// circle: (event: CircleEvent) => void;
// }
类
成员
字段
// 空
class Point {}
// 字段
class Point {
x: number;
y: number;
}
// 初始化
class Point {
x = 0;
y = 0;
}
// 打算在构造函数外初始化一个字段
class OKGreeter {
name!: string;
}
--strictPropertyInitialization:设置控制类字段是否需要在构造函数中初始化
readonly
防止修改
class Greeter {
readonly name: string = "world";
constructor(otherName?: string) {
if (otherName !== undefined) {
this.name = otherName;
}
}
err() {
// 报错
this.name = "not ok";
}
}
构造器
class Point {
x: number;
y: number;
// Normal signature with defaults
constructor(x = 0, y = 0) {
this.x = x;
this.y = y;
}
}
class Point {
// Overloads
constructor(x: number, y: string);
constructor(s: string);
constructor(xs: any, y?: any) {
// TBD
}
}
// 超类调用
class Base {
k = 4;
}
class Derived extends Base {
constructor() {
// Prints a wrong value in ES5; throws exception in ES6
console.log(this.k); // 报错
super();
}
}
getter/setter
class C {
_length = 0;
get length() {
return this._length;
}
set length(value) {
this._length = value;
}
}
// 从 TypeScript 4.3 开始,可以使用不同类型的访问器来获取和设置
class Thing {
_size = 0;
get size(): number {
return this._size;
}
set size(value: string | number | boolean) {
let num = Number(value);
// Don't allow NaN, Infinity, etc
if (!Number.isFinite(num)) {
this._size = 0;
return;
}
this._size = num;
}
}
推断规则:
- 如果 get 存在但没有 set,则属性自动为 readonly
- 如果不指定 setter 参数的类型,则从 getter 的返回类型推断
- getter 和 setter 必须有相同的 成员可见性
索引签名
class MyClass {
[s: string]: boolean | ((s: string) => boolean);
check(s: string) {
return this[s] as boolean;
}
}
继承
- implements:是否满足接口
- extends:继承父类
成员可见性
- public:在任何地方访问
- protected:类的子类可见
- private 类似于 protected,但不允许从子类访问成员
静态成员
class MyClass {
static x = 0;
static printX() {
console.log(MyClass.x);
}
}
console.log(MyClass.x);
MyClass.printX();
// 使用相同的 public、protected 和 private 可见性修饰符
class MyClass {
private static x = 0;
}
console.log(MyClass.x); // 报错
// 继承
class Base {
static getGreeting() {
return "Hello world";
}
}
class Derived extends Base {
myGreeting = Derived.getGreeting();
}
// static 类中的块
class Foo {
static #count = 0;
get count() {
return Foo.#count;
}
static {
try {
const lastInstances = loadLastInstances();
Foo.#count += lastInstances.length;
}
catch {}
}
}
为什么没有静态类
- TypeScript(和 JavaScript)没有一个名为
static class的构造,就像 C# 一样 - 这些构造之所以存在,是因为这些语言强制所有数据和函数都在一个类中;因为 TypeScript 中不存在该限制,所以不需要它们。只有一个实例的类通常只表示为 JavaScript/TypeScript 中的普通对象
- 例如,不需要 TypeScript 中的 “静态类” 语法,因为常规对象(甚至顶层函数)也可以完成这项工作:
// Unnecessary "static" class
class MyStaticClass {
static doSomething() {}
}
// Preferred (alternative 1)
function doSomething() {}
// Preferred (alternative 2)
const MyHelperObject = {
dosomething() {},
};
泛型类
class Box<Type> {
contents: Type;
constructor(value: Type) {
this.contents = value;
}
}
const b = new Box("hello!"); // const b: Box<string>