typescript进阶语法

配置文件tsconfig.json

目标文件

在项目中执行命令tsc --init可生成ts编译配置文件tsconfig.json。该配置文件默认在针对根目录下的所有ts文件进行编译时,才会使用,也即tsc命令后面带上了ts文件路径,该配置不会生效。

ts-node 带文件路径执行ts文件时,会使用到该配置文件

配置文件tsconfig.json中如果不配置编译的目标文件,默认会编译整个跟目录下的ts文件。配置目标文件的参数包括以下三种:

  • include:需要编译的文件路径
  • exclude:编译时需要忽略的文件
  • files:类似include

上述三种属性属于tsconfig.json的根属性,与compilerOptions同级。三种属性的数据格式都是字符串(包括正则)数组。

编译选项

配置文件中的compilerOptions为编译器选项,包含编译时的偏好配置,包括输出路径ES标准是否删除注释等选项。

完整的配置选项含义见网址:https://www.tslang.cn/docs/handbook/compiler-options.html

联合类型

联合类型(Union Types)可以通过管道(|)将变量设置多种数据类型,赋值时可以赋值指定的多种数据类型。但使用时只能使用多种数据类型的共性属性或方法,如:

interface human {
    eat: boolean;
    clever: boolean;
    say: boolean;
}

interface animals {
    eat: boolean;
    clever: boolean;
}

function sport(object: human | animals) {
    console.log(object.eat);
    console.log(object.clever);
    console.log(object.say);    //报错
}

如果需要在函数中针对某些特定情况,对联合类型不一样的属性或方法进行使用,可以使用断言(as)方式进行。上述中的sport函数可以如下写法:

function sport(object: human | animals) {
    if (object.clever) {            //或'clever' in object,或typeof、instanceof(接口不可以使用)
        console.log((object as human).say)
    }
}

枚举类型

数字枚举类型

使用enum关键字定义枚举类型,当枚举的值为数字类型时,具有相互映射的特性,并且枚举值依次递增:

enum Color {
    RED,
    BLUE = 2,
    GREEN
}

console.log(Color.RED, Color.BLUE, Color.GREEN);      //0 2 3
console.log(Color[0], Color[2], Color[3]);      //RED BLUE GREEN
console.log(Color);            //{ '0': 'RED', '2': 'BLUE', '3': 'GREEN', RED: 0, BLUE: 2, GREEN: 3 }

混合枚举类型

而当枚举值不是数字类型,如字符串类型时,只有正向映射,没有反向映射(可以混合使用,不过字符串后的属性需要自定义值)。如:

enum Color {
    RED,
    BLUE = 'blue',
    GREEN = 2
}

console.log(Color.RED, Color.BLUE, Color.GREEN);      //0 'blue' 2
console.log(Color);            //{ '0': 'RED', '2': 'GREEN', RED: 0, BLUE: 'blue', GREEN: 2 }

函数计算式

枚举值也可以使用函数计算式,但不可以与字符串同时使用:

enum Color {
    RED,
    // BLACK = '123'      //会报错
    BLUE,
    GREEN = Math.random()
}

console.log(Color.RED, Color.BLUE, Color.GREEN);      //0 1 0.890438315724388
console.log(Color);
/**
{ '0': 'RED',
  '1': 'BLUE',
  RED: 0,
  BLUE: 1,
  GREEN: 0.92861417038683,
  '0.92861417038683': 'GREEN'
}
*/

函数泛型

定义

当函数不知道参数与返回类型,或者考虑到函数的复用性时,可以使用泛型来对不同的输入、输出类型进行定义与复用,而具体的类型只有在使用函数的时候才会知道。

function identity<T, P>(arg: T, arg1: P): T {
  return arg;
}
identity<string, number>('123', 123);

泛型的定义,也可以使用联合类型,上述中的TP在函数使用的时候都可以是带|的联合类型。

泛型也可以用于类中声明,例如:

class DataManager<T> {
    constructor(private data: T[]) { }
    getIterm(index: number): T {
        return this.data[index];
    }
    addIterm(value: T): void {
        this.data.push(value);
    }
}

let data = new DataManager<string>(['1']);
console.log(data.getIterm(0));
// data.addIterm(1)      //报错。已经定义了泛型为string

typeofPartial在泛型中的应用:

class User {
  userId: number;
  userName: string;
}
type User1 = keyof User;  // 'userId' | 'userName'
type User2 = Parttial<User>; // {userId?:number, userName?:string}

继承

泛型可以继承接口,使泛型具有某些特定的属性或方法。例如:

interface Iterm {
    name: string
}

class DataManager<T extends Iterm> {
    constructor(private data: T[]) { }
    getIterm(index: number): string {
        return this.data[index].name;
    }
}

let data = new DataManager<{ name: string, value: string }>([{ name: '123', value: '456' }]);
console.log(data.getIterm(0));

类型约束

泛型还可以对函数进行类型约束,例如:

function hello<T>: (params: T) => {
      return params;
}
const func: <T>(param: T) => T = hello;      //此时hello函数是与func相同的泛型配置,所以可以进行赋值

泛型还可以配合keyof,对输入输出进行安全类型控制。

interface Person {
    name: string;
    age: number;
}

class Teacher {
    constructor(private person: Person) { };
    getName(key: string) {
        return this.person[key];
    }
}

上述代码中,由于getName中的参数key可取任意字符串参数,所以会导致this.person[key]可能出现问题,在TS这种强类型中认为是一个隐患问题。此时可以使用泛型与keyof进行改写,例如:

interface Person {
    name: string;
    age: number;
}

class Teacher {
    constructor(private person: Person) { };
    getName<T extends keyof Person>(key: T): Person[T] {
        return this.person[key];
    };
}

命名空间

定义

使用关键字namespace可定义命名空间,该空间内可包含变量、方法、类、接口等数据结构(甚至是子命名空间)。如需要通过该命名空间将成员暴露出去,则需要添加关键字export

namespace Person {
    class Man {
        constructor(private username: string) { }
    };
    class Weman {
        constructor(private username: string) { }
    };
    export function sayHello(): void {            //外部可通过Person.sayHello进行函数调用
        console.log('hello')
    }
}

调用

命名空间之间的相互调用时,在ts文件的开始位置,使用/// <reference path="SomeFileName.ts">进行声明,然后进行调用。也可通过ES6的exportimport from进行暴露与引用:

/** person.ts文件 */
export namespace Person {
      export class Sport{ };
      export var canSay = true;
}

/** man.ts文件 */
import {Person} from 'person.ts';

let sports = new Person.Sport();
console.log(Person.canSay);

上述中的模块引出代码也可使用commonJS规范,使用module.exports = Person进行引出,不过需要事先安装@types/node

描述文件

定义

当引入一个三方包时,该模块包可能是由JS所编写的,不能直接被typescript进行使用,这是就需要类型描述文件*.d.ts进行翻译后,才可以对模块进行使用。常用的三方包模块的翻译文件可通过@types/<包名>进行安装,其引用的是仓库https://github.com/DefinitelyTyped/DefinitelyTyped.git

如果没有翻译文件,则需要手动进行翻译文件的编写。使用declare进行全局变量或全局函数的定义声明,配合浏览器或Node使用ts文件。如:

// Jquery.d.ts
declare function $(param: () => void): void;            //定义全局变量
declare function $(param: string): {      //定义全局函数
    html: (html: string) => {}
}
declare namespace $ {      //定义全局对象
    namespace fn {
        class init { }
    }
}

// page.ts
$(function () {
    alert('123');
    $('body').html('<body>123</body>');
    new $.fn.init();
});

模块化使用

声明文件配合原JS模块进行使用时,一般通过模块化的方式进行编写。如安装了模块jquery,此时不使用官方的@types/jquery声明文件,编写自定义的声明文件如下:

// jquery.d.ts
declare module 'jquery' {            // 与原模块名一致
      function $(param: () => void): void;
      function $(param: string): {
            html: (html: string) => {}
      }
      namespace $ {
            namespace fn {
                  class init { }
            }
      }
      export = $      //导出
}

// page.ts
import $ from 'jquery'

$(function () {
    alert('123');
    $('body').html('<body>123</body>');
    new $.fn.init();
});
posted @ 2021-08-20 16:36  Mr_Kahn  阅读(56)  评论(0)    收藏  举报