基本类型
- string
- 语法:
let str: string = 'string'; - number
- 语法:
let number: number = 123; - boolean
- 语法:
let boolean: boolean = true; - 元组
- 语法:
tuple: [string, number] = ['1', 2]; - ts新增的类型,是一种特殊的数组,被限定了数组内成员的个数和类型。
- 元组越界:
let tuple: [number, number] = [1, 2]; tuple.push(3); // 元组是可以通过push添加元素的 tuple[2]; // Error, 但是元组无法访问push添加的元素 - 数组
- 语法:
let arr: number[] = [1,2,3]; // 通过「泛型接口」定义数组, 联合类型可以声明多种数组成员类型。 let arr2: Array<number | string> = [1,2,3,'4']; - symbol
- 语法:
let symbol: symbol = Symbol(); - object
- 语法:
let obj: object = {}; obj.x = 'x'; //Error, 因为object没有定义具体属性,所以不能访问或修改属性。 let obj2: {x: number, y: number} = {x: 1, y: 2}; // 声明具体属性 obj2.x = 3; - undefined / null
- 语法:
let undefined1: undefined = undefined; let null1: null = null; undefined / null是一切基本类型的子类型,其他基本类型可以赋值成null / undefined,null / undefined不能赋值给其他基本类型。- void
- 语法:
function fn(x: number, y: number): void {} - 表示没有任何返回值,通常用于表示函数没有任何返回值。
- 注意:在js中void是一个操作符,它可以让任何表达式返回
undefined。 - never
- 语法:
function fn(): never {throw new Error('error')}; function fn2(): never {while(true){}}; - 表示永远不会有返回值,通常用于表示两种类型函数内抛出错误(Error)、函数内部存在死循环(while(true))。
- any
- 语法:
let any: any = '222'; - 最宽泛的类型,可以表示任意的类型。
枚举
声明方式:
emun E {
X, // 如果第一个成员没有初始化的值,默认为0
Y
}枚举成员的类型:
- 数字枚举成员
// 如果有枚举成员初始化为数字常量,那之后的枚举成员的值会根据默认值递增 enum E { X, //0 Y = 2, //2 Z //3 } - 字符串枚举成员
// 使用字符串初始化的枚举成员,之后的枚举成员必须赋予初始值 enum E { X: 'X', Y: 'Y' } - 联合枚举成员
enum E { X: 'X', Y: 1 } - 计算枚举成员
enum E { X: 'X' } enum E2 { Y: E.X, Z: (1+2)/3, W: 'string', // Error, 计算枚举成员不可以和字符串枚举成员同时存在 }
枚举成员可以作为「类型注解」:
enum E {
X,
Y
}
let x: E.X = 30; // 数字枚举成员可以和 number 类型相互兼容。
enum E2 {
X = 'X',
Y = 'Y',
}
let x2: E2.X = 'X'; // Error, 字符串枚举成员和string类型不兼容。
let x3: E2.X = E2.X;
let x4: E2 = E2.X;
let x5: E2 = E2; // Error枚举类型可以当作值传递:
enum E {
X,
Y
}
function fn(obj: { X: number }) {
return obj.X;
}
fn(E); // 枚举类型在编译之后就是js中的对象,此时就相当于传递一个对象。常量枚举:
普通枚举在ts编译阶段会被编译成不同的对象,而常量枚举在ts编译阶段会被删除,使用枚举成员的地方会被替换成相应的枚举成员的值。
const enum E {
X,
Y
}函数
声明方式:
// 1.在函数体内声明
function fn1(x: number, y: number): number {return x+y};
// 2.在变量中声明, 声明中的变量名可以和函数体内函数名不同
let fn2: (x: number, y: number) => number
fn2(a, b) {return a+b};
// 3.用「类型别名」声明
type fn3Type = (x: number, y: number) => number
let fn3: fn3Type = (a, b) => (a + b);
// 4.用「接口」声明
interface fn4Inter {
(x: number, y: number): number
}
let fn4: fn4Inter = (a, b) => (a + b)ts中的函数规则:
- 形参和实参需要一一对应。
function fn(x:number, y:number):number {return x+y}; fn(1); // Error, 形参中的y没有传入。 fn(1,2); - 函数参数可以有「可选参数」,「可选参数」必须在「必填参数」之后。
function fn(x: number, y?:number): number {return x+6}; fn(1); function fn2(x?:number, y:number): number {return y+6}; //Error - 参数可以有默认值(可选参数可以为其设置默认值)。
function fn(x: number, y?:number=0): number {return x+y}; - 和es6一样函数参数可以有「剩余参数」。
function fn(x: number, ...y: number[]) {} fn(1,2,3); - 与es6不同,ts中函数可以重载。
function(x:number,y:number):number function(x:string,y:string):string function(x:any,y:any):any { // 在最宽泛的版本中实现这个函数 }
类
声明方式:与es6中的类声明方式相同
class Animal {
construtor(name) {
this.name = name;
}
name: string // 声明类的成员属性类型
run() {}
}ts中类的规则:
- 类的继承
class Dog extends Animal { construtor(name, age) { super(name); // 派生类函数一定要有super的调用,super代表父类的实例。 this.age = age; } age: number } - 类的成员修饰符(ts对es的扩展)
- public: 公有成员
public str:string - protected: 受保护成员(只能在类或者子类中访问)
protected str: string - private: 私有成员(子类、实例都不可以访问)
private str: string - readonly: 只读成员(只能访问不能修改)
readonly str: string - static: 静态成员(只能通过类名.属性值访问)
static str: string
注意:constructor 如果被设置了 private、protected会怎么样?
设置为private, 类即不能被实例化也不能被继承。设置为protected, 类只能被继承不能被实例化。
class Parent {
public str1: string = 'st1';
protected str2: string = 'st2';
private str3: string = 'st3';
readonly str4: string = 'st4';
static str5: string = 'st5';
}
class Children extends Parent {
construtor() {
super();
this.str1; // success
this.str2; // success
this.str3; // error
this.str4 = 'str'; // error
this.str5; // error
Parent.str; // success
}
}
let parent = new Parent();
parent.str1; // success
parent.str2; // error
parent.str3; // error
parent.str4 = 'str'; // error
parent.str5; // error
Parent.str5; // success
// constructor private
class Parent {
private constructor() {}
}
let parent = new Parent(); // error
class Children extends Parent {} // error
// constructor protected
class Parent {
protected constructor() {}
}
let parent = new Parent(); // error
class Children extends Parent {} // success- 抽象类(ts对es的扩展,用于实现类的多态)
抽象类通常作为其他类的基类使用,它不可以被实例化,子类可以重写基类中的方法。
抽象类中还可以包含抽象方法,如果被声明为抽象方法,那么它必须在子类中声明。
abstract class Parent {
abstract str;
abstract run(): void; // 抽象方法只在基类中声明,具体实现要在子类中实现
}
class Children extends Parent {
str: string = 'str';
run(): string {
return 'str';
}
}接口
接口是ts中的一种用来约束函数、对象、类的结构和类型的方法。
声明方式:
interface A {
x: string,
y: number
}ts中接口的规则:
- 接口成员属性:只读属性 && 可选属性
interface A{ readonly x: string , //只读属性 y?: string, // 可选属性 } // y 作为可选属性,可以不定义 let obj: A = { x: 'string' } obj.x = 'aaaa'; // error, x 作为只读属性不可以修改 - 接口索引签名: 使用索引签名约束一类值的类型
// 字符串索引 interface A { y: number, // error, 其他属性约束的类型应该在索引类型包含的范围之内 [key: string]: string } let obj: A = { x: 'string' } // 字符串索引 interface B { [index: number]: string } let arr: B = ['1', '2'] - 接口约束:
- 约束对象
interface InterObj { x: string, y: number | string, } let obj: InterObj = { x: 'string', y: 10 } - 约束函数
// 不仅可以约束函数,还可以约束函数的属性 interface InterFn { (x: number, y: number): number doSomething(): void version: string } let fn: InterFn = ((x, y) => {return x + y}) as InterFn; fn.doSomething = () => {}; fn.version = '1.0'; - 约束类
interface InterClass { state: boolean } // 接口描述静态部分和实例部分 // 接口在约束类时只能描述实例属性interface ClockInterface { // new (h: number, m: number); Error 构造器属于静态属性 tick(); } class Clock implements ClockInterface { constructor(h: number, m: number) {} tick() { console.log('1'); } } // 类作为参数传递时,可以使用构造器描述类 interface ClockClassInterface { new (h: number, m: number): ClockInterface } function create(ctor: ClockClassInterface, h, m): ClockInterface { return new cotr(h, m) } - 继承:
- 「接口」继承「接口」
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
let square = <Square>{}; // 这样写可以先定义对象类型,再赋值
square.color = "blue";
square.sideLength = 10;
- 「接口」继承「类」
// 如果接口继承的类中有private、protected属性,当前接口只能被类的子类使用
class Control {
private state: any;
protected state2: any;
}
interface SelectableControl extends Control {
select(): void;
}
class Button extends Control implements SelectableControl {
// 使用接口成功
}泛型
让函数和类支持多种类型,不预先确定数据类型,具体类型在使用时确定
声明方式:
- 泛型定义函数
// 你可以把T当作反省变量,它可以是任意所有类型 // identity定义了一个接收参数和返回值需要一样的函数 function identity<T>(arg: T): T { return arg; } indentity('string'); // 可以隐式的让ts来判断泛型变量的类型 <string>indentity('string'); // 还可以显式的直接声明出来泛型变量的类型 - 泛型定义接口
// 第一种写法,把泛型对象仍然写在函数上 interface GenericInentityFn { <T>(arg: T): T } let myIdentity: GenericIdentityFn = (arg) => arg; // 第二种写法,泛型接口定义在接口上 interface GenericIdentityFn<T> { (arg: T): T; } let myIdentity: GenericIdentityFn<number> = (arg) => arg; // 定义在接口上后,使用接口时必须定义泛型变量的类型 - 泛型定义类
class GenericNumber<T> { constructor(arg: T) {} zeroValue: T; add(x: T, y: T) => T } new GenericNumber<number>(); new GenericNumber(20);
泛型约束:
我们使用了泛型变量,但是我们并不知道反省变量有什么属性,我们也就无法使用它的属性,我们使用泛型约束可以去限制传入的泛型变量要符合条件才可以。
interface LogInter5 {
length: number;
}
function log5<T extends LogInter5>(result: T): T {
console.log(result.length); //如果没有约束会报错
return result;
}类型检查机制
- 类型推断
- 类型兼容
- 类型保护
// 类型保护
enum Type {
Strong,
Weak
}
class Java {
helloJava() {
console.log('hello Java')
}
}
class Javascript {
helloJs() {
console.log('hello JS');
}
}
function isJava(lang: Java|Javascript)lang is Java {
return (lang as Java).helloJavas !== undefined;
}function isJava(lang: Java | Javascript, x?: number|string) { let lang = type === Type.Strong ? new Java() : new Javascript();
// 1. instanceof 判断
if(lang instanceof Java) { lang.helloJava();
}else {
lang.helloJavascript();
}
// 2. 类型谓词 判断
if(isJava(lang)) {
lang.helloJava();
}else {
lang.helloJavascript();
}
// 3. typeof 基本类型判断
if(typeof x == 'string') {
x.length;
}else {
x.toFixed();
}
}高级类型
- 交叉类型 和 联合类型
交叉类型是将多个类型合并为一个类型。
interface A {
x: string
}
interface B {
y: string
}
let obj: A & B = {
x:'string',
y: 'string'
}联合类型是表示一个值可以是几种类型之一。
interface A {
x: string
}
interface B {
y: string
}
let obj: A | B = {
x:'string',
}
let obj2: A | b = {
y: 'string'
}- 索引类型
索引类型编译器能够检查使用了动态属性名的代码。
- 索引类型查询操作符:
keyof T的结果为T上已知道公共属性名的联合interface Person { name: string age: number } let personProps: keyof Person; // 'name' | 'age' - 索引返回操作符:
T[k]的接口返回T上已知道属性K的类型interface Person { name: string age: number } let personProps: Person['name'] // number - 具体例子🌰:
// 返回对应key的值 function getValues<T, K extends keyof T>(obj: T, keys: K[]): T[K][] { return keys.map(key => obj[key]);} let obj9 = {a: 1, b: 2, c: 3}; getValues(obj9, ['a', 'b']); getValues(obj9, ['c', 'd']) // Error, 索引类型可以判断出来T中的属性名不存在c和d
- 映射类型
映射类型是从旧类型中创建新类型的一种方式。
// 把所有属性变成只读, K in 相当于遍历T中的所有类型
type Readony<T> = {
readonly [K in keyof T]: T[K]
}
// 把所有属性变成可选
type Partial<T> = {
[K in keyof T]?: T[K]
}
// 过滤部分属性
type Pick<T, K extends keyof T> = {
[P in K]: T[K]
}
// 创建新属性
type Record<K extends keyof any, T> = {
[P in K]: T
}- 条件类型
有的条件类型会以一个条件表达式进行类型关系检测,从两种类型中选择其中一种。
T extends U ? X : Y// 条件类型立即被解析的例子 type TypeName<T> = T extends string ? "string" : T extends number ? "number" : T extends boolean ? "boolean" : T extends undefined ? "undefined" : T extends Function ? "function" : "object";type T0 = TypeName<string>; // "string" type T1 = TypeName<"a">; // "string" type T2 = TypeName<true>; // "boolean" type T3 = TypeName<() => void>; // "function" type T4 = TypeName<string[]>; // "object" // 条件类型被推迟解析的例子 interface Foo { propA: boolean; propB: boolean; } declare function f<T>(x: T): T extends Foo ? string : number; function foo<U>(x: U) { // 这里因为无法确定x的具体类型,所以a的类型为'U extends Foo ? string : number' let a = f(x); // This assignment is allowed though! let b: string | number = a; }- 分布式条件类型
(A | B)extends U ? X : Y 会被解析为(A extends U ? X : Y)| (B extends U ? X : Y)