快速上手请移步至:TS从入门到出门
TypeScript
数据类型
- 凡是可以使用父类型的地方都可以使用子类型,反之不行。
基本数据类型
string、number、boolean、null、undefined、bigint、symbol
- number与bigint类型不兼容。
let name: string = 'AAA';
let age: number = 9;
let isBoy: boolean = true;
let n: null = null;
let u: undefined = undefined;
let x: bigint = 123n;
- undefined和null是所有类型的子类型
数组
- 数组成员可以动态变化。
- 如果没有声明数组类型,会自动推断类型。
- const声明的数组成员可以改变,如果不允许修改可设置为只读readonly。
- 使用泛型的数组无法直接使用readonly,有两个专门的泛型用来生成只读数组。
- readonly只允许用在数组和元组类型的参数前面。
let arr1: string[] = ['a', 'b', 'c'];
let arr3: (number | string)[] = [1, 'A'];
let arr2: Array<string> = ['a', 'b', 'c'];
let arr4: Array<number | string> = [1, 'A'];
//number表示最底层的数组成员类型是number
let arr: number[][] = [[1, 2, 3],[4, 5, 6]];
//arr5: (number | strting)
let arr5 = [1, 2, 3, 'AAA'];
const arr6: number[] = [1, 2, 3];
arr6[0] = 0;
const arr7: readonly number[] = [1, 2, 3];
//报错
const arr8: readonly Array<number> = [1, 2, 3];
const arr9: ReadonlyArray<number> = [1, 2, 3];
const arr10: Readonly<number[]> = [1, 2, 3];
const arr11 = [1, 2, 3] as const;
symbol
- unique symbol表示单个的、某个具体的Symbol值,使用const声明。
let x: symbol = Symbol();
let y: symbol = Symbol();
x === y;//false
//以下等价
const x: unique symbol = Symbol();
const x = Symbol();
特殊数据类型
any、unknown、never、enum、tuple
any
- any类型(顶层类型)没有任何限制,可以赋予任意类型值。
- TypeScript如果无法推断出类型,就会认为该变量是any。
- any类型有可能会“污染”其他变量。
let x: any = 'This can be any type';
x = 'AAA';
x = 9;
x = true;
//污染 不会报错
let x: any = 'AAA';
let y: number;
y = x;
y * 123;
y.toFixed()
unknown
- unknown类型(顶层类型)可以解决any导致的“污染”问题。
- unknown类型不能直接赋值给其他类型的变量。
- unknown类型不能调用属性和方法。
- unknown类型只能进行比较运算、取反、typeof、instance of。
- 确定unknown的类型之后可以进行调用与其他运算。
let x: unknown = 'This can be any type too';
x = 'AAA';
x = 9;
x = true;
//防止污染 会报错
let x: unknown = 'AAA';
let y1: number = x; //报错
let y2: boolean = x; //报错
let x: unknown = {name:'AAA'};
x.name; //报错
let x: unknown = 9;
x + 1; //报错
x === 9; //true
let x: unknown = 9;
if (typeof x === 'number'){
let ten = x + 1;
}
let x: unknown = 'AAA';
if (typeof x === 'string'){
let len = x.length;
}
never
- never类型为空,永远不会发生的值的类型。
- never类型(底层类型)可以赋值给任意类型。
function throwError (message: string): never {
throw new Error(message);
}
function fn(x: string | number) {
if (typeof x === 'string'){
}else if (typeof x === 'number') {
}else {
x;
}
}
enum
TypeScript新增数据类型枚举
- enum既是一种类型也是一个值,编译后会变成对象留在代码中。
- 所有成员值均为只读。
- 同名enum会合并,只允许一个的首成员省略初值,且不能有同名成员
- 枚举可以正向引用也可以反向引用。
- 适用于成员的值不重要,名字更重要的场景。
- 使用时考虑用断言替代。
- Keyof运算符可以取出Enum结构的所有成员名,作为联合类型返回。
enum Color {
Red, //0
Green, //1
Blue //2
}
let c = Color.Green; //1
let c = Color['Green'] //1
let c: Color = Color.Green;
let c: number = Color.Green;
//赋值
const enum Color {
Red, //0
Green, //1
Blue //2
}
const enum Color {
Red = 3, //3
Green = 6, //6
Blue = 9 //9
}
const enum Color {
Red = 9, //9
Green = 9, //9
Blue = 9 //9
}
const enum Color {
Red = 9, //9
Green, //10
Blue //11
}
const enum Color {
Red, //0
Green = 9, //9
Blue //10
}
//同名enum会合并,只允许一个的首成员省略初值,且不能有同名成员
const enum Color {
Red,
}
const enum Color {
Green = 9,
}
const enum Color {
Blue = 10,
}
const enum Color {
Red,
Green = 9,
Blue = 10
}
//成员的值可以设为字符串,但是未设置类型的成员,默认为数值且必须位于字符串成员之前
const enum Color {
Red,
Green = 'AAA',
Blue = 10
}
const enum Color {
Red = 'AAA',
Blue = 'BBB'
}
//'Red' | 'Blue'
type Foo = keyof typeof Color;
//反向取值
enum Color {
Red,
Blue
}
console.log(Color[1]); //Blue
tuple
- 成员类型可以自由设置的数组。
- 必须明确声明每个成员的类型。
- 数组的成员类型写在方括号外面,元组的成员类型写在方括号里面。
- 必须明确给出类型声明,不能省略,否则TypeScript会把一个值自动推断为数组。
- 类型后加?表示类型可选。
- 可选成员必须位于必选成员之后。
- 越界会报错而数组不会。
- 使用扩展运算符...可以表示不限制成员数量的元组,可以用在元组的任意位置,它后面只能是一个数组或元组,(不建议)失去了元组的意义。
- 可以添加成员名,但没有实际作用。
- 只读元组。
const s: [string, string, boolean] = ['A', 'B', true];
let a: [number, number?] = [1];
//成员不限制
type NamedNums = [
string,
...number[]
]
const a: NamedNums = ['A', 1];
const b: NamedNums = ['A', 1, 2];
const c: NamedNums = ['A'];
type t1 = [string, number, ...boolean[]];
type t2 = [string, ...boolean[], number];
type t3 = [...boolean[], string, number];
//添加成员名
type Color = [
red: number,
green: number,
blue: number
];
const c:Color = [255, 255, 255];
//读取成员类型
type Tuple = [string, number];
type Age = Tuple[1];//number
//只读元组
//写法一
type t = readonly [number, string];
//写法二
type t = Readonly<[number, string]>;
值类型
单个值也是一种类型,称为“值类型”。
let x: 'hello';
x = 'hello';//true
x = 'hi';//false
联合数据类型
多种类型组合为新类型。
let x:string|number;
x = 123;//true
x = 'hello';//true
let rainbowColor: '赤' | '橙' | '黄' | '绿' | '青' | '蓝' | '紫'
交叉数据类型
多个类型组成的新类型。任何一个类型必须既满足又满足。
- 主要用于对象合成。
let x: number&string//never 不可能既是number又是string
let obj: {foo: string} & {bar: string};
obj = {
foo: 'hello',
bar: 'world'
}
ES6类型
Map<K,V>
let map1 = new Map();//Key any, value any
let map2 = new Map<string, number>();//Key string, value number
const myMap: Map<boolean,string> = new Map([
[false, 'no'],
[true, 'yes'],
]);
async函数
const p:Promise<number> = /* ... */;
async function fn(): Promise<number> {
var i = await p;
return i + 1;
}
类型断言
对于没有声明的值,TypeScript会进行类型推断,使用类型断言可以直接告知编译器是什么类型。
- 指定unknown类型变量的类型。
- 必须满足值是类型断言的子类型或者类型断言是值的子类型。
- 如果没有声明变量类型,let声明的变量会被类型推断为基本类型之一,const声明的变量会被推断为值类型常量。
- as const 断言后相当于使用const命令声明,使用其断言后,值不能再改变。只能用于字面量,不能用于变量,也不能用于表达式。
- 可以用于整个对象,也可以用于对象的单个属性。
//写法一
let a: T = <T>b;
//写法二 推荐
let a: T = b as T;
type T = 'a' | 'b' | 'c';
let foo = 'a';
let bar: T = foo as T;
//对象中使用类型断言
const obj: {x: number} = {x: 0, y: 0} //报错
const obj: {x: number} = {x: 0, y: 0} as {x: number}//true
const obj: {x: number} = {x: 0, y: 0} as {x: number, y: number}//true
const value: unknown = 'Hello';
const s1: string = value;//error
const s1: string = value as string;//true
//const使用
let s = 'Hello';
type Greet = | 'Hello' | 'Hi';
function sayHello (greet: Greet) {}
sayHello(s);//error
//方法一
const s = 'Hello';
//方法二
let s = 'Hello' as const;//使用后s的值不可改变
type Greet = | 'Hello' | 'Hi';
function sayHello (greet: Greet) {}
sayHello(s);//true
const v1 = {
x: 1,
y: 2
};//{x: number, y: number}
const v2 = {
x: 1 as const,
y: 2
};//{x: 1, y: number}
const v3 = {
x: 1,
y: 2
} as const;//{readonly x: 1, readonly y: 2}
const a1 = [1, 2, 3];//number[]
const a2 = [1, 2, 3] as const;//readonly [1, 2, 3]
非空断言
保证变量不为空,写法是在变量名后面加!
function f (x?: number|null) {
validateNumber(x);
console.log(x!.toFixed());
}
function validateName (e?: number|null) {
if (typeof e !== 'number')
throw new Error('not a number');
}
断言函数
一种特殊的函数,用于保证函数参数符合某种类型。
function isString (value: unknown): void {
if (typeof value !== 'string')
throw new Error('not a string');
}
function toUpper (x: string|number) {
isString(x);
return x.toUpperCase();
}
//新写法
function isString (value: unknown): asserts value is string {
if (typeof value !== 'string')
throw new Error('not a string');
}
object
- 一旦声明了类型,对象赋值时,就不能缺少指定的属性,也不能有多余的属性。
- 类型声明中的属性不能删除只能修改。
- 使用?:表示该属性是可选的,如果可选属性未被赋值,返回undefined,但可选属性与undefined不等价。
- 属性名前加readonly表示只读,只读属性只能在对象初始化期间赋值,如果属性是对象,可以修改改对象属性,但是不能替换改对象。
- 如果一个对象有两个引用,其中一个变量是可写的,一个变量是只读的,那么从可写变量修改属性,会影响到只读变量。
- 结构类型原则:只要对象B满足对象A的结构特征,TypeScript就认为对象B兼容对象A的类型。
- 不能动态添加属性,即使是一个空对象。
const obj: {name: string, age:number} = {name: 'AAA', age: 9}
const obj: {x: number, y: number, add(x: number, y: number): number} = {
x: 1,
y: 1,
add(x, y) {
return x + y;
}
}
type MyObj = {
name: string,
age: number
}
const obj: MyObj = {name: 'AAA', age: 9}
interface MyObj {
name: string,
age: number
}
const obj: MyObj = {name: 'AAA', age: 9}
const obj: {name: string, age?: number} = {name: 'AAA'}
const obj: {readonly name: string, age: number} = {name: 'AAA', age: 9}
type Obj = {readonly name: string, readonly age: number}
const obj: Obj = {name: 'AAA', age: 9}
//可写影响只读
interface Person {
name: string,
age: number
}
interface ReadonlyPerson {
readonly name: string;
readonly age: number
}
let p1: Person = {
name: 'AAA',
age: 9
}
let p2: ReadonlyPerson = p1;
p1.age += 1;
p2.age //10
//解构
const {id, name, age}: {
id: number,
name: string,
age: number
} = user;
type A = {
x: number
}
type B = {
x: number,
y: number
}
const B = {
x: 1,
y: 1
}
const A: {x: number} = B;
function
- 函数的类型声明,需要在声明函数时,给出参数的类型和返回值。TypeScript可以推断返回值类型。
- 函数的实际参数个数,可以少于类型指定的参数个数,但是不能多于。
- 默认参数写法与JavaScript写法一致。
- 如果函数的某个参数可以省略,参数名后加问号。
- 可选参数不能与默认值同时使用。
- 具有默认值的参数如果不位于参数列表的末尾,调用时不能省略,如果要触发默认值,必须显示传入undefined。
- readonly只读。
- 高阶函数:一个函数的返回值还是一个函数,那么前一个函数就称为高阶函数。
//写法一
const hello = function (txt: string) {
console.log('hello' + txt);
}
//写法二
const hello: (txt: string) => void = function (txt) {
console.log('hello' + txt);
}
//写法二修
type MyFunc = (txt: string) => void;
const hello: MyFunc = function (txt) {
console.log('hello' + txt);
}
function add (
x: number,
y: number
){
return x + y;
}
const myAdd: typeof add = function (x, y) {
return x + y;
}
//函数本身还有属性
let foo: {
(x: number): void;
version: string
} = f;
//使用接口
interface myfn {
(a: number, b: number): number;
}
var add: myfn = (a, b) => a + b;
//默认参数
function createPoint (
x: number = 0,
y: number = 0
): [number, number] = {
return [x, y];
}
createPoint();
//可选参数
function f(x?: number) {
return x;
}
f();//true
f(9);//true
f(undefined);//true
//error
function f(x?: number = 0){}
function add (
x: number = 0,
y: number
){
return x + y;
}
add(1) //error
add(undefined,1)//true
//参数解构
function f (
[x, y]: [number, number]
){}
function sum (
{a, b, c} = {
a: number;
b: number;
c: number
}
){console.log(a + b + c)}
//结合type解构
type ABC = {
a: number;
b: number;
c: number
}
function sum({a, b, c}: ABC) {console.log(a + b + c)}
//rest参数
//数组
function joinNumbers (...nums: number[]) {}
//元组
function f (...args: [number, boolean?]) {}
//嵌套
function f (...args: [boolean, ...string[]]) {}
//只读
function arraySum (
arr: readonly number[];
)
//抛出错误
function throwErr (): void {
throw new Error ('error');
}
function throwErr (msg: string): never {
throw new Error (msg);
}
//高阶函数
(someValue: number) => (multiplier: number) => someValue * multiplier;
箭头函数
const repeat = (
str: string,
times: number
): string => str.repeat(times);
function hello (
fn: (a: string) => void;
): void {
fn('hello')
}
type Person = {name: string}
const people = ['A', 'B', 'C'].map(
(name):Person => ({name})
)
函数重载
函数重载:有些函数可以接受不同类型或者不同个数的参数,并且根据参数的不同,会有不同的函数行为。
- 每个类型声明之间,以及类型声明与函数实现的类型之间,不能有冲突。
- 类型最宽的声明放在最后面,防止覆盖其他类型声明。
//函数重载
function reverse (str: string): string;
function reverse (arr: any[]): any[];
function reverse (
stringOrArray: string|any[];
): string|any[] {
if (typeof stringOrArray === 'string')
return stringOrArray.split('').reverse().join('');
else
return stringOrArray.slice().reverse();
}
function fn (x: boolean): void;
function fn (x: string): void;
function fn (x: number|string){}//error
function fn (x: boolean|string){}//true
//重载声明排序
function f(x:any):number;
function f(x:string): 0|1;
function f(x:any):any {};
const a:0|1 = f('hi'); // error
//对象方法重载
class StringBuilder {
#data = '';
add (num: number): this;
add (bool: boolean): this;
add (str: string): this;
add (value: any): this {
this.#data += String(value);
return this;
}
toString() {
return this.#data;
}
}
构造函数
const d = new Date();
class Animal {
numLegs: number = 4;
}
type AnimalConstructor = new () => Animal;
function create (c: AnimalConstructor): Animal {
return new c();
}
const a = create(Animal);
class
-
类可以在顶层声明也可以在构造函数内部声明。
-
属性前加readonly表示属性只读,实例对象不可修改,构造函数可以修改。
-
构造函数不能返回声明值类型。
-
如果某个属性只有get方法,没有set方法,那么该属性自动成为只读属性。
-
类允许定义属性索引,如果一个对象同时定义了属性索引和方法,那么前者必须包含后者类型。属性存取器视同属性。
-
interface接口或type别名可以用对象的形式,为class指定一组检查条件。然后类使用implements关键字,表示当前类满足这些外部类型条件的限制。
- implements只是指定检查条件,如果不满足条件就会报错,并不能代替class自身的类型声明。
- implements后面如果是类,该类被视为接口。
- 可以实现多个接口。
-
类和接口同名,接口会被合并进类。
-
作为类型使用时,类名只能表示实例的类型,不能表示类的自身类型。
-
结构类型原则:一个对象只要满足Class的实例结构,就跟该Class属于同一类型。
-
两个类的实例结构相同,那么这两个类就是兼容的,可以用在对方的使用场合。
-
类也可以写成泛型。
- 静态成员不能使用泛型的类型参数。
-
类前面加关键字abstract 表示该类不能被实例化,只能当做其他类的模版,称为抽象类。
- 抽象类可以继承其他抽象类。
- 抽象类属性前面加abstract 表示子类必须给出该方法的实现。
- 一个子类最多继承一个抽象类。
- 抽象成员前面不能有private,否则无法再子类中实现该成员。
class User {
name: string;
age: number
}
//给出初值,不写类型会自动推断属性类型
class User {
name: 'A';
age: 9
}
class User {
name!: string;
age!: number
}
//readonly方法一
class User {
readonly id: number = 1;
}
//readonly方法二
class User {
readonly id: number;
constructor () {
this.id = 1;
}
}
//函数重载
class Point {
constructor (x: number, y: string);
constructor (s: string);
constructor (xs: number|string, y?: string){
}
}
//存取器方法
class User {
_name = 'A';
get name () {
return this._name;
}
}
const u = new User();
u.name = 'B';//error
//属性索引
class MyClass {
[s: string]: boolean | ((s: string) => boolean);
get (s: string) {
return this[s] as boolean;
}
}
class MyClass {
[s: string]: boolean | (() => boolean);
f () {
return true;
}
}
class MyClass {
[s: string]: boolean;
get isInstance () {
return true;
}
}
//implements
interface Country {
name: string;
captial: string
}
type Country {
name: string;
captial: string;
}
class MyCountry implements Country{
name = '';
captial = '';
id = 1;
}
//类与接口同名
class A {
x: number = 1;
}
interface A {
y: number;
}
let a = new A();
a.y = 10;
//类表示类型
class Point {
x: number;
y: number;
constructor (x: number, y: number) {
this.x = x;
this.y = y;
}
}
//error
function createPoint (
PointClass: Point, //error
x: number,
y: number
){return new PointClass(x, y)}
//true
function createPoint (
PointClass: typeof Point,//true
)
//结构类型原则
class Foo {
id!: number;
}
function fn (arg: foo) {}
const bar = {
id: 9,
amount: 199
};
fn(bar);//true
//实例结构相同相互兼容
class Person {
name: string;
}
class Customer {
name: string;
}
const cust: Customer = new Person();
//泛型类
class Box<Type> {
contents: Type;
constructor(value: Type) {
this.contents = value;
}
}
const b: Box<string> = new Box('A');
//抽象类
abstract class A {
id = 1;
}
class B {
amount = 100;
}
const b = new B();
b.id;//1
b.count;//100
abstract C extends A{
}
类的继承
-
子类可以用于类型为基类的场合。
-
子类可以覆盖基类的同名方法。
- 子类同名方法不能与基类的类型定义相冲突。
-
extends后面的类型是构造函数均可实现继承。
class A {
greet () {
console.log('A');
}
}
class B extends A {
}
const b = new B();
b.greet();//A
const a:A = b;
a.greet();
//覆盖
class B extends A {
greet(name?: string) {
if (name === 'undefined') {
super.greet()
}else {
console.log(`hello ${name}`);
}
}
}
//error
class B extends A {
greet(name: string) {
console.log(`hello ${name}`);
}
}
class A {
protected x: string = '';
protected y: string = '';
protected z: string = '';
}
class B extends A {
public x: string = '';//true
protected y: string = '';//true
private z: string = '';//error
}
//extends后是构造函数
class MyArray extends Array<number> {};
class MyError extends Error {};
interface
-
interface可以表示对象的各种语法:
-
对象属性
-
对象的属性索引
- 一个接口中,最多只能定义一个字符串索引一个数值索引。索引会约束该类型中所有名字为字符串的属性。
- 如果一个接口中同时定义了字符串索引和数值索引,那么数值索引必须服从于字符串索引。
-
对象方法
-
函数
-
构造函数
-
-
多个同名接口会合并成一个接口。
-
同名接口合并时,同一个属性如果有多个类型声明,彼此不能有类型冲突。
-
同名接口合并时,如果同名方法有不同的类型声明,那么会发生函数重载,且后面的定义比前面的定义具有更高的优先级。
- 同名方法中,如果有一个参数是字面量类型,字面量类型具有更高的优先级。
-
-
与type的区别
- type能够表示非对象类型,而interface只能表示对象类型(包括数组、函数等)。
- interface可以继承其他类型,type不支持继承。
- 同名interface会合并,同名type会报错。
- interface不能包含属性映射,type可以。
- this关键字只能用于interface。
- type可以扩展原始数据类型,interface不行。
- type无法表示复杂类型(交叉类型和联合类型)。
interface User {
name: string;
age: 9
}
const u: User = {
name: 'A',
age: 9
}
type A = User['name'];//string
//对象属性
interface User {
readonly id: number;
name: string;
age?: number
}
//对象的属性索引
//字符索引
interface User {
[prop: string]: number;
}
//数值索引
interface A {
[prop: number]: string;
}
const obj: A = ['a', 'b', 'c'];
//既有字符索引又有数值索引
interface A {
[prop: string]: number;
[prop: number]: string;//error
}
interface B {
[prop: string]: number;
[prop: number]: number;
}
//对象的方法
//一
interface A {
f (x: boolean): string;
}
//二
interface B {
f: (x: boolean) => string;
}
//三
interface C {
f: {(x: boolean): string};
}
//四
const f = 'f';
interface A {
[f] (x: boolean): string;
}
//重载
interface A {
f(): number;
f(x: boolean): boolean;
f(x: string, y: string): string;
}
function MyFunc(): number;
function MyFunc(x: boolean): boolean;
function MyFunc(x: string, y: string): string;
function MyFunc(
x?: boolean | string,
y?: string
): number | boolean | string {
if (x === undefined && y === undefined) return 1;
if (typeof x === 'boolean' && typeof y === 'boolean') return true;
if (typeof x === 'string' && typeof y === 'string') return 'hello';
throw new Error('error');
}
const a: A = {
f: MyFunc
}
//函数
interface Add {
(x: number, y: number): number;
}
const myAdd: Add = (x, y) => x + y;
//构造函数
interface ErrorConstructor {
new (message?: string): Error;
}
//同名重载
interface Cloner {
clone(animal: Animal): Animal;
}
interface Cloner {
clone(animal: Sheep): Sheep;
}
interface Cloner {
clone(animal: Dog): Dog;
clone(animal: Cat): Cat;
}
//等同于
interface Cloner {
clone(animal: Dog): Dog;
clone(animal: Cat): Cat;
clone(aniaml: Sheep): Sheep;
clone(animal: Animal): Animal;
}
interface继承
-
interface继承interface
- 子接口与父接口的同名属性必须是类型兼容的,不能有冲突。
- 多重继承时,如果多个父接口存在同名属性,那么这些同名属性不能有类型冲突。
-
interface继承type
- type定义的类型不是对象,interface就无法继承。
-
interface继承class
//interface 继承 interface
interface Style {
color: string;
}
interface Shape {
name: string;
}
interface Circle extends Style, Shape {
radius: number;
}
//interface 继承 type
type Country = {
name: string;
capital: string;
}
interface CountryWithPop extends Country {
population: number;
}
//interface 继承 class
class A {
x: string = '';
y(): boolean {
return true;
}
}
interface B extends A {
z: number;
}
const b: B {
x: '',
y: function(){return true},
z: 123
}
泛型
-
带有”类型参数“。类型声明需要的变量,需要在调用时传入具体的参数类型。
-
类型参数可以设置默认值。
- 一旦类型参数有默认值,就表示它是可选参数,可选参数必须在必选参数之后。
-
尽量减少使用,类型参数越少越好。
function getFirst<T> (arr: T[]) {
return arr[0];
}
getFirst<number>([1, 2, 3]);
function comb<T> (arr1: T[], arr2: T[]): T[] {
return arr1.concat(arr2);
}
comb<number|string>([1, 2], ['a', 'b']);
//多参数
function map<T, U> (
arr: T[],
f: (arg: T) => U
): U[] {
return arr.map(f);
}
map<string, number>(['1', '2', '3'], (n) => parseInt(n));//[1, 2, 3]
//接口的泛型写法
//写法一
interface Box<Type> {
contents: Type;
}
let box: Box<string>
interface Comparator<T> {
compareTo(value: T): number;
}
class Rectangle implements Comparator <Rectangle> {
compareTo(value: Rectangle): number {}
}
//写法二
interface Fn {
<Type>(arg: Type): Type;
}
function id <Type> (arg:Type): Type {
return arg;
}
let myId: Fn = id;
//类的泛型写法
class Pair <K, V> {
key: K;
value: V;
}
const Container = class<T> {
constructor (private readonly data: T) {}
};
const a = new Container<boolean>(true);
const b = new Container<number>(0);
//类型别名的泛型写法
type Container<T> = {value: T};
const a: Container<number> = {value: 0};
const b: Container<string> = {value: 'A'}
//树形结构
type Tree<T> = {
value: T;
left: Tree<T> | null;
right: Tree<T> | null
}
function getFirst <T = string> (
arr: T[]
): T {return arr[0]}
namespace
-
namespa用来建立一个容器,内部的所有变量和函数,都必须在这个容器里面使用。
-
多个同名的namespace会自动合并。
- 命名空间中的非export的成员不会被合并,但它们只能在各自的命名空间中使用。
-
命名空间可以与同名函数合并,但同名函数必须声明于命名空间之前。同名的命名空间相当于给函数对象添加额外的属性。
namespace Utils {
function isString (value: any) {
return typeof value === 'string';
}
export function isNumber (value: any) {
return typeof value === 'number';
}
isString('hello');//true
}
Utils.isString('hi');//false 只能在内部使用
Utils.isNumber(9);//true export可以在外部使用
namespace App {
import isNumber = Utils.isNumber;
isNumber(9);//true
}
namespace App {
function isBoolean (value: boolean) {
return typeof value === 'boolean';
}
export function sayHello () {
console.log('Hello');
}
}
装饰器
-
一种语法结构,用来在定义时修改类的行为。
-
特征
- 前缀为@后面为表达式。
- @后的表达式必须是一个函数(执行后可以得到一个函数)。
- 这个函数接受所修饰对象的一些相关值作为参数。
- 函数要么不返回值,要么返回一个新对象取代所修饰的目标对象。
- 类执行前会先执行装饰器,并且会向装饰器自动传入参数。
-
装饰器函数的类型定义
-
value:所修饰的对象。
-
context:上下文对象。
-
kind:所装饰的对象类型。
-
class:类装饰器一般用来对类进行操作,可以不返回任何值。
- 类装饰器可以返回一个函数,替代当前类的构造方法。
-
method:方法装饰器用来装饰类的方法。
-
getter:针对类的取值器(getter)。
-
setter:针对类的存值器(setter)。
-
field:属性装饰器用来装饰定义在类顶部的属性。
- 属性装饰器要么不返回值,要么返回一个函数,该函数会自动执行,用来对所装饰属性进行初始化。
-
accessor:为属性x自动生成存取值器和存值器,它们作用于私有属性x。
-
-
name:字符串或Symbol值,所装饰对象的名字,比如类名、属性名等。
-
addInitializer():函数,用来添加类的初始化逻辑。
-
private:布尔值,表示所装饰的对象是否为类的私有成员。
-
static:布尔值,表示所修饰的对象是否为类的静态成员。
-
access:一个对象,包含某个值的get和set方法。
-
-
-
执行顺序
-
评估:计算@符号后面的表达式的值,得到的应该是函数。
-
应用:把评估装饰器后得到的函数应用于所装饰对象。
- 方法装饰器->属性装饰器->类装饰器
-
function simpleDecorator ( target: any, context: any) {
console.log('hi' + target);
return target;
}
@simpleDecorator
class A {} //hi
//装饰器函数的类型定义
type Decorator = (
value: DecoratedValue,
context: {
kind: string;
name: string | symbol;
addInitializer?(initializer: () => void): void;
static?: boolean;
private?: boolean;
access: {
get?(): unknown;
set?(value: unknown): void;
};
}
) => void | ReplacementValue;
//类装饰器
function Greeter(value, context) {
if (context.kind === 'class') {
value.prototype.greet = function () {
console.log('hello');
};
}
}
@Greeter
class User {}
let u = new User();
u.greet(); // "hello"
//方法装饰器
function trace(decoratedMethod) {
//
}
// `@trace` 等同于
// C.prototype.toString = trace(C.prototype.toString);
class C {
@trace
toString() {
return 'C';
}
}
//属性装饰器
function logged(value, context) {
const { kind, name } = context;
if (kind === 'field') {
return function (initialValue) {
console.log(`initializing ${name} with value ${initialValue}`);
return initialValue;
};
}
}
class Color {
@logged name = 'green';
}
const color = new Color();
// "initializing name with value green"
//accessor装饰器
class C {
accessor x = 1;
}
//等同于
class C {
#x = 1;
get x() {
return this.#x;
}
set x(val) {
this.#x = val;
}
}
类型映射
-
将一种类型按照映射规则,转换成另一种类型。
-
映射修饰符会原样复制原始对象的可选属性和只读属性。
-
- +? +readonly
-
- -? -readonly
-
-
键名重映射
- 属性过滤,可以过滤掉某些属性。
type A = {
foo: number;
bar: number;
}
type B = {
foo: string;
bar: string;
}
//映射
type B = {
[Prop in keyof A]: string;
}
type MyObj = {
[p in string]: boolean;
}
//等同于
type MyObj = {
[p: string]: number;
}
//将属性改为可选属性
type A = {
a: string;
b: number;
};
type B = {
[Prop in keyof A]?: A[Prop];
}
//将属性改为只读属性
type Readonly <T> = {
readonly [P in keyof T]: T[P];
}
type A = {
a?: string;
readonly b: number;
}
type B = {
[Prop in keyof A]: A[Prop];
}
//等同于
type B = {
a?: string;
readonly b: number;
}
// 添加可选属性
type Optional<Type> = {
[Prop in keyof Type]+?: Type[Prop];
};
// 移除可选属性
type Concrete<Type> = {
[Prop in keyof Type]-?: Type[Prop];
};
// 添加 readonly
type CreateImmutable<Type> = {
+readonly [Prop in keyof Type]: Type[Prop];
};
// 移除 readonly
type CreateMutable<Type> = {
-readonly [Prop in keyof Type]: Type[Prop];
};
//键名重映射
type A = {
foo: number;
bar: number;
};
type B = {
[p in keyof A as `${p}ID`]: number;
};
// 等同于
type B = {
fooID: number;
barID: number;
};
//过滤属性
type User = {
name: string,
age: number
}
type Filter<T> = {
[K in keyof T as T[K] extends string ? K : never]: string
}
type FilteredUser = Filter<User> // { name: string }
declare
declare关键字用来告诉编译器,某个类型是存在的,可以在当前文件中使用,且不用给出具体实现。
declare不会出现在编译后的文件里。
- 变量(const、let、var 命令声明)
- type 或者 interface 命令声明的类型。
- class
- enum
- 函数(function)
- 模块(module)
- 命名空间(namespace)
//变量
declare let x:number;
x = 1;
//函数
declare function sayHello(
name:string
):void;
sayHello('张三');
//error
function sayHello(
name:string
):void;
function sayHello(name) {
return '你好,' + name;
}
//类
declare class Animal {
constructor(name:string);
eat():void;
sleep():void;
}
//命名空间
declare namespace AnimalLib {
class Animal {
constructor(name:string);
eat():void;
sleep():void;
}
type Animals = 'Fish' | 'Dog';
}
//模块
declare module AnimalLib {
class Animal {
constructor(name:string);
eat(): void;
sleep(): void;
}
type Animals = 'Fish' | 'Dog';
}
//enum
declare enum E1 {
A,
B,
}
declare enum E2 {
A = 0,
B = 1,
}
declare const enum E3 {
A,
B,
}
declare const enum E4 {
A = 0,
B = 1,
}
参考:阮一峰《TypeScript教程》