乐趣区

关于typescript:Ts入门笔记

TS- 入门学习笔记

TypeScript 是 JavaScript 的一个超集,次要提供了类型零碎和对 ES6 的反对。
与 js 相比,最大的有点是类型零碎的引入,因为 js 自身是弱类型语言,所以人造不存在类型零碎,这一方面使得 js 应用起来更简略,但同时也带来了肯定的问题,所以 ts 从新引入了类型零碎,带了了以下的长处:

  • 类型零碎实际上是最好的文档,大部分的函数看看类型的定义就能够晓得如何应用了
  • 能够在编译阶段就发现大部分谬误,这总比在运行时候出错好
  • 加强了编辑器和 IDE 的性能,包含代码补全、接口提醒、跳转到定义、重构等

ts 的文档比拟多
中武官网:https://www.tslang.cn/docs/ho…
官网还提供了一些例子:https://github.com/Microsoft/…
入门教程:https://ts.xcatliu.com/basics…
深刻了解:https://jkchao.github.io/type…

类型零碎

TS 最重要的类型零碎,除了囊括了最根本的根底类型外,更引入了接口,类,泛型等
更多高级类型见:https://www.tslang.cn/docs/ha…

根本类型

根本类型的变量申明和 js 没有太大区别,就是减少了类型的申明

let isDone: boolean = false; // 布尔型
let decLiteral: number = 6; // 浮点数
let name: string = "bob"; // 字符串
let sentence: string = `Hello, my name is ${name}.` // 模板字符串
let unusable: void = undefined; // void 示意空,能够是 undefined 或者 null
let u: undefined = undefined; //undefined 类型的变量只能被赋值为 undefined
let n: null = null; //null 类型的变量只能被赋值为 null

须要留神的,申明一个 void 类型的变量没有什么用,因为你只能将它赋值为 undefined 和 null,与 void 的区别是,undefined 和 null 是所有类型的子类型。也就是说 undefined 类型的变量,能够赋值给 number 类型的变量

// 这样不会报错
let num: number = undefined;
// 这样也不会报错
let u: undefined;
let num: number = u;

数组

数组类型有多种定义形式

let list: number[] = [1, 2, 3]; //「类型 + 方括号」let list: Array<number> = [1, 2, 3]; // 数组泛型 Array<elemType>

// 接口模式
interface NumberArray {[index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];

let list:(number | string)[] =  [1, 2, 3,'4'];  // 联结类型的数组
let list: any[] = ['Xcat Liu', 25, { website: 'http://xcatliu.com'}]; // 容许数组中呈现任何类型的 any,不过这种数组原则上不算规范的数组。

须要留神,类数组不是数组类型,比方 arguments,NodeList 等,所以无奈应用数组类型来申明

任意值

任意值(Any)用来示意容许赋值为任意类型。类型零碎的存在就是为了让变量在申明初就决定好本人的类型,如果是一个一般类型,在赋值过程中扭转类型是不被容许的,然而 any 类型,则容许被赋值为任意类型(和 js 很像)

let aAnyNumber: any = '哈哈哈哈';
aAnyNumber = 2333;

类型推论

let x = 3; 
// 等价于
let x: number = 3;

// 当须要从几个表达式中推断类型时候,会应用这些表达式的类型来推断出一个最合适的通用类型。例如,let x = [0, 1, null];
// 等价于
let x:number[] = [0, 1, null];

如果定义的时候没有赋值,不论之后有没有赋值,都会被推断成 any 类型而齐全不被类型查看

联结类型

联结类型(Union Types)示意取值能够为多种类型中的一种(类型的或概念),用 | 来划分

let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;

当 TypeScript 不确定一个联结类型的变量到底是哪个类型的时候,咱们只能拜访此联结类型的所有类型里共有的属性或办法:

function getLength(something: string | number): number {return something.length;}
//length 不是 string 和 number 的共有属性,所以会报错。

联结类型的变量在被赋值的时候,会依据类型推论的规定推断出一个类型:

let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
console.log(myFavoriteNumber.length); // 5
myFavoriteNumber = 7;
console.log(myFavoriteNumber.length); // 编译时报错
// 被赋值为 string 的 myFavoriteNumber 类型 被推断成了 string,拜访它的 length 属性不会报错。// 而从新被赋值为 7 的 myFavoriteNumber 的类型被推断成了 number,拜访它的 length 属性时就报错了。

接口(interface)

与 java 的接口相似,它是对行为的形象, 申明一种类型的数据,而具体如何口头须要由类(classes)去实现。
TS 中接口的作用就是为这些行为的形象类型命名和为你的代码或第三方代码定义契约

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

let tom: IPerson = {
    name: 'Tom',
    age: 25
};

接口个别首字母大写,倡议接口前能够加上 I

失常状况申明的接口的里的变量都是必填,赋值的时候,变量的形态必须和接口的形态保持一致,不能多也不能少

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

let tom: IPerson = {name: 'Tom'};
//error
let tom: IPerson = {
    name: 'Tom',
    age: 25,
      sex:'male'
};
//error

可选属性

当然,有时候接口里的属性不全都是必须的。有些是只在某些条件下存在,或者基本不存在。这时候能够应用可选属性

interface IPerson {
    name: string;
    age?: number;  // 应用问号申明可选属性,可选属性的含意是该属性能够不存在。}

let tom: IPerson = {name: 'Tom'};

任意属性

当咱们可能确定这个对象还会带有任意数量的其它属性时,能够定义额定的属性

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

interface ISquareConfig {
    color?: string;
    width?: number;
    [propName: string]: any;
}
//SquareConfig 能够有任意数量的属性,并且只有它们不是 color 和 width,那么就无所谓它们的类型是什么。interface IPerson {
    name: string;
    age?: number;
    [propName: string]: string;
}
// 实际上下面一段代码会报错,类型“number”的属性“age”不能赋给字符串索引类型“string”。

只读属性

一些对象属性只能在对象刚刚创立的时候批改其值。能够在属性名前用 readonly 来指定只读属性

interface IPerson {
    readonly id: number;
    name: string;
    age?: number;
}

let tom: IPerson = {
    id: 89757,
    name: 'Tom'
};

办法继承接口

咱们能够应用接口的形式来定义一个函数须要合乎的形态:

interface SearchFunc {(source: string, subString: string): boolean;
}

let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {return source.search(subString) !== -1;
}

类实现接口

大多数状况下,咱们会把一些类中的公共办法形象成一个接口,而后由类来实现这个接口

interface Alarm {alert();
}
interface Light {lightOn();
    lightOff();}
class Car implements Alarm, Light { // 一个类能够实现多个接口
    alert() {console.log('Car alert');
    }
    lightOn() {console.log('Car light on');
    }
    lightOff() {console.log('Car light off');
    }
}

接口与接口之间能够是继承关系:

interface Alarm {alert();
}
interface LightableAlarm extends Alarm {lightOn();
    lightOff();}

函数

Ts 中的函数和 js 相比,减少了入参的类型校验和返回后果的类型校验。

// 一个函数表达式
let myAdd: (x: number, y: number) => number =
    function(x: number, y: number): number {return x + y;};
// 在 TypeScript 的类型定义中,=> 用来示意函数的定义,右边是输出类型,须要用括号括起来,左边是输入类型。let myAdd = function(x: number, y: number): number {return x + y;};
// 理论史昂,下面的函数表达式也是没有问题的,函数的输入输出类型都申明了,右边的函数变量会依据类型推断主动增加类型
// 一个函数申明
function sum(x: number, y: number): number {return x + y;}

可选参数和默认参数

和接口定义相似,输出多余的(或者少于要求的)参数,是不容许的,然而同样能够应用可选属性
与接口中的可选属性相似,咱们用 ? 示意可选的参数:

function buildName(firstName: string, lastName?: string) {if (lastName) {return firstName + ' ' + lastName;} else {return firstName;}
}

可选参数必须接在必须参数前面。换句话说,可选参数前面不容许再呈现必须参数了

默认参数的能力是 ES6 提供的,在 ES6 中,咱们容许给函数的参数增加默认值,TypeScript 会将增加了默认值的参数辨认为可选参数。

function buildName(firstName: string = 'Tom', lastName: string) {return firstName + ' ' + lastName;}
// 此时就不受「可选参数必须接在必须参数前面」的限度了

残余参数

必要参数,默认参数和可选参数有个共同点:它们示意某一个参数。有时,你想同时操作多个参数,或者你并不知道会有多少参数传递进来。ES6 中,能够应用 …rest 的形式获取函数中的残余参数(rest 参数)

function push(array: any[], ...items: any[]) {items.forEach(function(item) {array.push(item);
    });
}

let a = [];
push(a, 1, 2, 3);

重载

学过 Java 或者 C ++ 的同学必定晓得函数重载,本来的 js 因为没有类型零碎,天然也就没有严格意义上的函数重载(尽管能通过传入不同的参数而返回不同类型的数据来模仿重载),TS 中引入了类型,尽管能够实现相似 java 的函数重载,然而因为最终要转化成 js,所以依然无奈实现真正的函数重载,只能在函数体内本人判断参数的个数和类型,而后实现不同的函数内容。

function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string {if (typeof x === 'number') {return Number(x.toString().split('').reverse().join(''));
    } else if (typeof x === 'string') {return x.split('').reverse().join('');
    }
}
// 如果定义了两个雷同名字的函数、接口或类,那么它们会合并成一个类型, 下面的函数的重载就用了这种形式

对于 TS 的函数重载能够看知乎的这篇探讨:https://www.zhihu.com/questio…

类(class)

类,定义了一件事物的形象特点,蕴含它的属性和办法

类的用法来源于 ES6 的 class,能够参考 ES6-class 的根本语法。
这里讲一下 TS 对类的补充。

拜访修饰符

ES6 中的类是没有拜访修饰符的,TypeScript 则 能够应用三种拜访修饰符(Access Modifiers),别离是 public、private 和 protected

public 润饰的属性或办法是私有的,能够在任何中央被拜访到,默认所有的属性和办法都是 public 的
private 润饰的属性或办法是公有的,不能在申明它的类的内部拜访
protected 润饰的属性或办法是受爱护的,它和 private 相似,区别是它在子类中也是容许被拜访的

同大多数的面向对象语言一样的用法,须要留神的是,因为 js 的人造起因,private 和 protected 在最终编译出的 js 中
其实是没有成果的, 也就是尽管 TS 的编译过程中会报错,然而如果你忽视掉这个报错编译进去的 js 代码其实是能够失常跑的

class Animal {
    private name;
    public constructor(name) {this.name = name;}
}

let a = new Animal('Jack');
console.log(a.name); // Jack
a.name = 'Tom';

// index.ts(9,13): error TS2341: Property 'name' is private and only accessible within class 'Animal'.
// index.ts(10,1): error TS2341: Property 'name' is private and only accessible within class 'Animal'.

抽象类

这块类的如果有 java 或者 c ++ 根底,应该会很好了解,抽象类做为其它派生类的基类应用。它们个别不会间接被实例化。
不同于接口,抽象类能够蕴含成员的实现细节。abstract 关键字是用于定义抽象类和在抽象类外部定义形象办法

1. 抽象类是不容许被实例化的
2. 抽象类中的形象办法不蕴含具体实现并且必须在派生类中实现

抽象类和接口有点相似,然而还是有不少区别的

1. 抽象类要被子类继承,接口要被子类实现.
2. 接口外面只能对办法进行申明,抽象类既能够对办法进行申明也能够对办法进行实现(抽象类中能够有非形象的办法)
3. 抽象类说到底还是类,领有类的所有性能(构造函数,拜访修饰符等),而接口办法默认修饰符是 public。你不能够应用其它修饰符

abstract class Animal {
    public name;
    public constructor(name) {this.name = name;}
    public abstract sayHi();}

class Cat extends Animal {public sayHi() {console.log(`Meow, My name is ${this.name}`);
    }
}

let cat = new Cat('Tom');

残缺的类

class Animal {
      name = 'Jack'; // 默认都为私有
      public publicName = 'publicJack'; // 私有属性能够在任何中央被拜访到
      private privateName = 'privateJack';// 公有属性不能在申明它的类的内部拜访
      readonly numberOfLegs: number = 8; // 应用 readonly 关键字将属性设置为只读的。只读属性必须在申明时或构造函数里被初始化
    public constructor(name) {this.name = name; // 构造函数}
      static isAnimal(a) { // 应用 static 修饰符润饰的办法称为静态方法,它们不须要实例化,而是间接通过类来调用
        return a instanceof Animal;
    }
    get name() {return 'Jack';}
    set name(value) {console.log('setter:' + value);
    }
}
let a = new Animal('Jack');
Animal.isAnimal(a); // true, 间接调用静态方法
console.log(a.sayHi()); // My name is Jack
console.log(a.privateName); //error,Property 'name' is private and only accessible within class 'Animal'
a.name = 'Tom'; // setter: Tom
console.log(a.sayHi()); // My name is Tom
// Cat 类继承于 Animal
class Cat extends Animal {constructor(name) {super(name); // 子类中应用 super 关键字来调用父类的构造函数和办法, 调用父类的 constructor(name) 
        console.log(this.name);
    }
    sayHi() {return 'Meow,' + super.sayHi(); // 调用父类的 sayHi()}
}

let c = new Cat('Tom'); // Tom
console.log(c.sayHi()); // Meow, My name is Tom

泛型

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

泛型是一个比较复杂和难以了解的货色,具体的能够参考 https://www.tslang.cn/docs/ha…

function createArray<T>(length: number, value: T): Array<T> {let result: T[] = [];
    for (let i = 0; i < length; i++) {result[i] = value;
    }
    return result;
}
// 函数名后增加了 <T>,其中 T 用来指代任意输出的类型,在前面的输出 value: T 和输入 Array<T> 中即可应用了。createArray<string>(3, 'x'); // ['x', 'x', 'x']
// 接着在调用的时候,能够指定它具体的类型为 string。当然,也能够不手动指定,而让类型推论主动推算出来:// 定义泛型的时候,能够一次定义多个类型参数:function swap<T, U>(tuple: [T, U]): [U, T] {return [tuple[1], tuple[0]];
}

swap([7, 'seven']); // ['seven', 7]

泛型束缚

在函数外部应用泛型变量的时候,因为当时不晓得它是哪种类型,所以不能随便的操作它的属性或办法:

function loggingIdentity<T>(arg: T): T {console.log(arg.length);
    return arg;
}
// index.ts(2,19): error TS2339: Property 'length' does not exist on type 'T'.

上例中,泛型 T 不肯定蕴含属性 length,所以编译的时候报错了。
这时,咱们能够对泛型进行束缚,只容许这个函数传入那些蕴含 length 属性的变量。这就是泛型束缚.
之前也说了,接口能够用来形容约束条件,所以咱们能够用接口来限度泛型的类型。

interface Lengthwise {length: number;}
function loggingIdentity<T extends Lengthwise>(arg: T): T {console.log(arg.length);
    return arg;
}

上例中,咱们应用了 extends 束缚了泛型 T 必须合乎接口 Lengthwise 的形态,也就是必须蕴含 length 属性。
此时如果调用 loggingIdentity 的时候,传入的 arg 不蕴含 length,那么在编译阶段就会报错了:

interface Lengthwise {length: number;}
function loggingIdentity<T extends Lengthwise>(arg: T): T {console.log(arg.length);
    return arg;
}
loggingIdentity(7);
// index.ts(10,17): error TS2345: Argument of type '7' is not assignable to parameter of type 'Lengthwise'.

泛型接口和泛型类

// 例子 1
function identity<T>(arg: T): T {return arg;}

let myIdentity: <T>(arg: T) => T = identity;

// 例子 2
interface GenericIdentityFn {<T>(arg: T): T;
}

function identity<T>(arg: T): T {return arg;}

let myIdentity: GenericIdentityFn = identity;

// 例子 3
interface GenericIdentityFn<T> {(arg: T): T;
}

function identity<T>(arg: T): T {return arg;}

let myIdentity: GenericIdentityFn<number> = identity;

例子 1 中,咱们在泛型函数中申明了一个 T 的泛型类型,在例子 2 中咱们吧这个对象字面量抽出来成为一个接口,例子 3 中,咱们更进一步,把泛型参数当作整个接口的一个参数。这样咱们就能分明的晓得应用的具体是哪个泛型类型(比方:Dictionary<string> 而不只是 Dictionary)。这样接口里的其它成员也能晓得这个参数的类型了。咱们不再形容泛型函数,而是把非泛型函数签名作为泛型类型一部分。当咱们应用 GenericIdentityFn 的时候,还得传入一个类型参数来指定泛型类型(这里是:number)。

与泛型接口相似,泛型也能够用于类的类型定义中,应用办法也基本一致。

class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) {return x + y;};

泛型参数的默认类型

咱们能够为泛型中的类型参数指定默认类型。当应用泛型时没有在代码中间接指定类型参数,从理论值参数中也无奈揣测出时,这个默认类型就会起作用。

function createArray<T = string>(length: number, value: T): Array<T> {let result: T[] = [];
    for (let i = 0; i < length; i++) {result[i] = value;
    }
    return result;
}

枚举

枚举(Enum)类型用于取值被限定在肯定范畴内的场景

enum Direction {
    Up,
    Down,
    Left,
    Right,
}
// 枚举成员会被赋值为从 0 开始递增的数字,同时也会对枚举值到枚举名进行反向映射
console.log(Direction['Up']) //0
console.log(Direction['Down']) //1
console.log(Direction[0]) //Up
console.log(Direction[1]) //Down

enum Direction {
    Up = 1,
    Down,
    Left,
    Right
}
// 当然也能够本人手动赋值,未手动赋值的枚举项会接着上一个枚举项递增
console.log(Direction['Up']) //1
console.log(Direction['Down']) //2

enum Direction {
    Up = "UP",
    Down = "DOWN",
    Left = "LEFT",
    Right = "RIGHT",
}
// 字符串枚举也是能够的

常量枚举

为了防止在额定生成的代码上的开销和额定的非间接的对枚举成员的拜访,咱们能够应用 const 枚举。常量枚举通过在枚举上应用 const 修饰符来定义

常数枚举与一般枚举的区别是,它会在编译阶段被删除,并且不能蕴含计算成员

const enum Directions {
    Up,
    Down,
    Left,
    Right
}

let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];

// 上例的编译后果是, 枚举值会在编译阶段间接替换

var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];

内部枚举

declare enum Directions {
    Up,
    Down,
    Left,
    Right
}
let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];

// 编译后果
var directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];

declare 定义的类型只会用于编译时的查看,编译后果中会被删除。

内部枚举与申明语句一样,常呈现在申明文件中。

字面量类型和类型别名

类型别名用来给一个类型起个新名字
字符串字面量类型用来束缚取值只能是某几个字符串中的一个。

类型别名与字符串字面量类型都是应用 type 进行定义

type Name = string; // 应用 type 将 string 的别名为 Name
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {if (typeof n === 'string') {return n;} else {return n();
    }
}
type EventNames = 'click' | 'scroll' | 'mousemove'; // 应用 type 申明字面量类型
function handleEvent(ele: Element, event: EventNames) {// do something}

handleEvent(document.getElementById('hello'), 'scroll');  // 没问题
handleEvent(document.getElementById('world'), 'dbclick'); // 报错,event 不能为 'dbclick'

模块

TS 的模块机制与 ES6 的模块机制根本保持一致(这里的模块咱们指“内部模块”,外部模块则成为下面所讲的命名空间),
个别以一个独立的文件为一个模块,任何蕴含顶级 import 或者 export 的文件都被当成一个模块。相同地,如果一个文件不带有
顶级的 import 或者 export 申明,那么它的内容被视为全局可见的(因而对模块也是可见的)

模块和命名空间相似,模块在其本身的作用域里执行,而不是在全局作用域里;这意味着定义在一个模块里的变量,函数,类等等
在模块内部是不可见的,除非你明确地应用 export 导出它们。

TS 的模块导入导出,能够参考(ES6 的模块机制)[https://es6.ruanyifeng.com/#d…],通过 export 和 import 来实现导入导出。

导出

应用 export 反对导出任何申明(比方变量,函数,类,类型别名或接口)

export interface StringValidator {isAcceptable(s: string): boolean;
}
export const numberRegexp = /^[0-9]+$/;

export class ZipCodeValidator implements StringValidator {isAcceptable(s: string) {return s.length === 5 && numberRegexp.test(s);
    }
}
export {ZipCodeValidator as mainValidator};// 对导出的局部重命名
declare let $: JQuery;
export default $; // 和 ES6 一样的默认导出

导入

通过 import 导入模块

import {ZipCodeValidator} from "./ZipCodeValidator";

import {ZipCodeValidator as ZCV} from "./ZipCodeValidator"; // 能够对导入内容重命名

import * as validator from "./ZipCodeValidator"; // 将整个模块导入到一个变量,并通过它来拜访模块的导出局部

import $ from "./Jquery"; // 导出模块的默认导出

CommonJS 反对

因为 ES6 语法并没有在浏览器端被齐全反对,所以咱们罕用的模块加载形式还是 commonjs,
为了反对 CommonJS 和 AMD 的 exports, TypeScript 提供了 export = 语法。export = 语法
定义一个模块的导出对象。这里的对象一词指的是类,接口,命名空间,函数或枚举。

若应用 export = 导出一个模块,则必须应用 TypeScript 的特定语法 import
module = require(“module”)来导入此模块(ES6 模块和 Commonjs 的混合??)。

// 导出
let numberRegexp = /^[0-9]+$/;
class ZipCodeValidator {isAcceptable(s: string) {return s.length === 5 && numberRegexp.test(s);
    }
}
export = ZipCodeValidator;


// 导入
import zip = require("./ZipCodeValidator");

// Some samples to try
let strings = ["Hello", "98052", "101"];

// Validators to use
let validator = new zip();

// Show whether each string passed each validator
strings.forEach(s => {console.log(`"${ s}" - ${validator.isAcceptable(s) ? "matches" : "does not match" }`);
});

如何创立模块

以下准则和 TS 没有太大关系,无论是 js 还是 Ts 都应遵循以下准则:

1. 尽可能地在顶层导出
2. 如果仅导出单个 class 或 function,应用 export default 当然也有习惯齐全不应用的 export default 的也没有问题
3. 如果要导出多个对象,把它们放在顶层里导出,相似上面
export class SomeType {// }
export function someFunc() { // }
4. 明确地列出导入的名字
import {SomeType, someFunc} from “./MyThings”;
5. 应用命名空间导入模式当你要导出大量内容的时候
import * as myLargeModule from “./MyLargeModule.ts”;
6. 应用从新导出进行扩大
7. 模块里不要应用命名空间

申明文件

什么是申明文件,简略的说申明文件就是用来申明你这个文件或者库里有哪些变量,哪些类,哪些函数和接口等等。

当咱们须要应用到第三方库时,咱们须要援用它的申明文件,这样 TS 能力晓得这些库有哪些接口和类,
能力取得对应的代码补全、接口提醒等性能

申明文件必须以 .d.ts 为后缀,外面蕴含了这个库相干的申明语句

援用申明文件

在咱们的我的项目开发中,如果要援用第三方库的话,个别还是须要援用它的申明文件,咱们能够间接从社区或者其余渠道
找到对应的申明文件,间接下载下来应用,当然更举荐的是应用 @types 对立治理第三方库的申明文件。
比方,获取 lodash 库的申明文件,只需应用上面的命令:

npm install --save @types/lodash

大多数状况下,类型申明包的名字总是与它们在 npm 上的包的名字雷同,然而有 @types/ 前缀,但如果你须要的
话,你能够在 https://aka.ms/types 这里查找 …。
当然,如果一个 npm 包公布的时候曾经蕴含了它的申明文件,那就不用再去下载相应的 @types 包了(判断根据是
package.json 中有 types 字段,或者有一个 index.d.ts 申明文件)

如果一个包自身既不蕴含申明文件,有没有再 @types 中公布申明文件,那么咱们就须要本人写申明文件了,举荐可
以创立一个 types 目录,专门用来治理本人写的申明文件。这种形式须要配置下 tsconfig.json 的 paths 和 baseUrl 字段。


{
    "compilerOptions": {
        "module": "commonjs",
        "baseUrl": "./",
        "paths": {"*" : ["types/*"]
        }
    }
}

书写申明文件

当一个第三方库没有提供申明文件或者咱们要公布咱们本人的包时,咱们就须要本人书写申明文件了。

申明文件,有申明语句组成,个别用 declare 来标示申明,对于变量,只有形容其类型,对于函数,须要形容其变量和返回

申明文件倡议能够把变量,类型和函数离开为三个文件

变量

应用 declare var 申明变量。如果变量是只读的,那么能够应用 declare const。你还能够应用 declare let 如果变量领有块级作用域。

/** 组件总数 */ 
declare var foo: number;
函数

应用 declare function 用来定义全局函数的类型

declare function greet(greeting: string): void;

应用 declare class 形容一个类或像类一样的对象。类能够有属性和办法,就和构造函数一样

declare class Greeter {constructor(greeting: string);

    greeting: string;
    showGreeting(): void;}
枚举

应用 declare enum 定义的枚举类型也称作内部枚举(Ambient Enums)

declare enum Directions {
    Up,
    Down,
    Left,
    Right
}
导出

npm 包的申明文件与全局变量的申明文件有很大区别。在 npm 包的申明文件中,应用 declare 不再会申明一个
全局变量,而只会在以后文件中申明一个局部变量。只有在申明文件中应用 export 导出,而后在应用方
import 导入后,才会利用到这些类型申明

  • export 导出变量
  • export namespace 导出(含有子属性的)对象
  • export default ES6 默认导出
  • export = commonjs 导出模块
export const name: string;
export function getName(): string;
export class Animal {constructor(name: string);
    sayHi(): string;}
export enum Directions {
    Up,
    Down,
    Left,
    Right
}
export interface Options {data: any;}

内置对象

JavaScript 中有很多内置对象,它们能够间接在 TypeScript 中当做定义好了的类型。
在浏览器环境下咱们能间接应用的对象有很多,上面只是一部分例子,具体可见 内置对象
类型相干:Boolean、Error、Date、RegExp
dom 相干:Document、HTMLElement、Event、NodeList

留神,TypeScript 外围库的定义中不蕴含 Node.js 局部。如果想用 TypeScript 写 Node.js,
则须要引入第三方申明文件 @types/node

深刻

深刻理解 Typescript 能够看上面这篇文章:https://jkchao.github.io/type…

退出移动版