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);
泛型的定义,也可以使用联合类型,上述中的T和P在函数使用的时候都可以是带|的联合类型。
泛型也可以用于类中声明,例如:
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
typeof与Partial在泛型中的应用:
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的export与import 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();
});

浙公网安备 33010602011771号