Typescript学习记录

317 阅读6分钟

环境配置

需要安装的包:

  • typescript
  • ts-node(可以直接运行ts文件)
npm i typescript ts-node -g

注意事项:

  • 用ts-node运行ts文件的时候可能会报 Cannot find module '@types/node/package.json',如果出现全局安装一下tslib@types/node这两个包即可。
  • 如果直接通过vscode右上角的运行按钮运行ts文件,以上所说的依赖包必须全局安装

日常类型

基础类型

string, number, boolean, null,undefined

数组类型

  • 「类型 + 方括号」表示法

    let fibonacci: number[] = [1, 1, 2, 3, 5];
    
  • 泛型方式

    let arr:Array<number> = []
    let arr = new Array<string>()
    
  • 接口方式

    interface NumberArray {
        [index: number]: number;
    }
    let fibonacci: NumberArray = [1, 1, 2, 3, 5];
    
  • 声明类数组

    interface ArgsArray {
        [index: number]: number;
        length: number;
        callee: Function;
    }
    function sum() {
        let args: ArgsArray = arguments;
    }
    

any/unkown

any - 任意的类型

unkown - 未知类型

let a:any
a = 1 // OK 
a = 'string' // OK 

let b:unkown
b = 1 // OK 
b = 'string' // OK 

let c:number = b // Error

这两个类型的使用方式大同小异,不同的是用unkown声明的变量不能赋值给别的变量

noImplicitAny

在ts.config中此项配置为true时,则需要显式添加any类型,反则为false的时候会隐式添加

// noImplicitAny=true
function a(x){} // Error
function a(x:any){} // Ok

// noImplicitAny=false
function a(x){} // OK

类型断言 (assertion)

类型断言(Type Assertion)可以用来手动指定一个值的类型。在你确定某个值的时候即可使用断言

语法

//值 as 类型
num as number

//<类型>值
<number>num

使用方式

// 案例1
interface Cat {
    name: string;
    run(): void;
}
interface Fish {
    name: string;
    swim(): void;
}

function swim(animal: Cat | Fish) {
    (<Fish>animal).swim()
}

// 案例2
class ApiError extends Error {
    code: string = '00';
}
class HttpError extends Error {
    statusCode: number = 200;
}

function isApiError(error: Error) {
    if (typeof (error as ApiError).code === 'string') {
        return true;
    }
    return false;
}

非空断言

当变量在上下文中无法判断类型的时候,表达式后缀加上!即可用于断言,该变量必定不会是nullundefined

// 不使用非空断言下面的代码会报错
function sum(x: number | null | undefined, y: number | null | undefined) {
    return x + y
}

// 使用后
function sum(x: number | null | undefined, y: number | null | undefined) {
    return x! + y!
}

// 也可以结合as一起做断言
function sum(x: number | string | null | undefined, y: number | null | undefined) {
    return (x as number)! + y!
}

接口(interface)

语法

interface 接口名称 {
	属性:属性类型
}

使用方式

// 声明
interface Person {
    name: string;
    age: number;
    getName() : string //定义函数
}
// 使用
let ivan :Person = {
    name:'ivan',
    age:18,
    getName(){
        return this.name
    }
}

任意属性

示例代码中任意属性的类型是string,其值的类型可为string或number。那么Person接口中就不能定义除string, numbe以外的类型否则就会报错。

ivan.address = '广东珠海'如果没有任意属性的情况下这段代码是会报错的

语法:

interface Person { 
	[任意名称:属性类型]:属性值的类型
}

示例:

interface Person {
    name: string;
    isBoy:boolean; // Error
    [propName: string]: string | number // 任意属性
}

let ivan :Person = {
    name:'ivan',
    age:18, // OK
    isChild:true, // Error
}
ivan.address = '广东珠海' // OK

注意:一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集。

只读属性

设置只读后该属性只允许读取行为,不允许进行赋值

语法:

interface Person { 
	readonly 属性:类型
}

示例:

interface Person {
    readonly id: number;
}

let ivan :Person = {
    id:1, // OK
}

ivan.id = 1 // Error

注意:是约束对象赋值,而不是约束对属性赋值

接口继承接口

语法:

interface 接口1 extendx 接口2 {
	...
}

示例:

interface Alarm {
    alert(): void;
}
interface LightableAlarm extends Alarm {
    lightOn(): void;
    lightOff(): void;
}
class Door implements LightableAlarm{
    lightOn(): void {
        throw new Error("Method not implemented.")
    }
    lightOff(): void {
        throw new Error("Method not implemented.")
    }
    alert(): void {
        throw new Error("Method not implemented.")
    }
}

接口继承类

当我们声明 interface Point3d extends Point 时,Point3d 继承的实际上是类 Point 的实例的类型。

换句话说,可以理解为定义了一个接口 Point3d 继承另一个接口 PointInstanceType

所以「接口继承类」和「接口继承接口」没有什么本质的区别。

语法:

interface 接口1 extendx 类1 {
	...
}

示例:

class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
}
interface PointInstanceType {
    x: number;
    y: number;
}

// 等价于 interface Point3d extends PointInstanceType
interface Point3d extends Point {
    z: number;
}


let point3d: Point3d = {x: 1, y: 2, z: 3};

function printPoint(p: PointInstanceType) {
    console.log(p.x, p.y);
}
printPoint(new Point(1, 2));

注意:接口只继承类的属性和方法,并不会继承constructor

泛型(generics)

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

简单的方式

函数名后添加了 <T>,其中 T 用来指代任意输入的类型,在后面的输入 value: T 和输出 Array<T> 中即可使用了。在调用函数的时候即可传入泛型的类型,如果不写则会按照参数的类型

function createArrayG<T>(value:T):Array<T>{
    let arr = []
    arr = Array(5).fill(value)
    return arr
}
console.log(createArrayG<number>(1)) // 输出:[1,1,1,1,1]

泛型中继承

T继承某个number后,那么调用函数的时候参数必须是number类型

function createArrayG2<T extends number>(value:T):Array<T>{
    let arr = []
    arr = Array(5).fill(value)
    return arr
}
console.log(createArrayG2(1)) // OK
console.log(createArrayG2('1')) // Error 

多个类型参数

泛型中可以定义多个不同的类型参数

function swap<T, U>(a: T, b: U): [U, T] {
    //return [a, b]; // Error
    return [b, a];
}

console.log(swap<number,string>(7, 'seven')); // OK
console.log(swap<number,string>('seven',7)); // Error
console.log(swap<number,string>('seven',true)); // Error

泛型约束

使用泛型变量的时候是无法知道该变量拥有什么属性和方法,假设你想通过length去查看变量的长度,ts会直接报错。

如果需要这样写的话则需要先定义一个interface然后声明一个length属性,然后通过泛型继承这个接口。

示例:

interface ILength {
    length: number;
}
// good
function loggingIdentity<T extends ILength>(arg: T) {
    console.log(arg.length);
}
// bad
function loggingIdentity<T>(arg: T) {
    console.log(arg.length);
}

loggingIdentity(1) // Error
loggingIdentity('hello'); // OK

注意:调用这个loggingIdentity时,arg必须要有length这个属性否则会直接报错

泛型接口

示例:

/* 方式1 */
interface CreateArrayFunc {
    <T>(length: number, value: T): Array<T>;
}
let createArrayIn: CreateArrayFunc;
createArrayIn = function <T>(length: number, value: T): Array<T> {
    let result: T[] = [];
    result = Array(length).fill(value);
    return result;
};
console.log(createArrayIn<number>(3, 1));
/* 方式2 */
interface CreateArrayFunc2<T> {
    (length: number, value: T): Array<T>;
}
let createArrayIn2: CreateArrayFunc2<any>;
createArrayIn2 = function <T>(length: number, value: T): Array<T> {
    let result: T[] = [];
    result = Array(length).fill(value);
    return result;
};
console.log(createArrayIn2(3, 1));

// 对象的接口定义
interface CreateObj<T> {
    name: T
}

let createObjI:CreateObj<string>

注意:当泛型参数提前到接口名上后,在使用泛型接口的时候,需要定义泛型的类型

泛型类

示例:

class GenericNumber<T> {
    zeroValue: T | undefined;
    add!: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function (x, y) {
    return x + y;
};
console.log(myGenericNumber.add(1, 2));

注意:如果属性用到了T,则这个属性需要加多一个undefined类型,或者使用!非空断言!

泛型参数的默认类型

示例:

function createArrayInf<T = string>(length: number, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}
console.log(createArrayInf(5, 1));

类(class)

窄化