TypeScript

1、简介

  TypeScript 是 JavaScript 的一个超集,主要提供了类型系统和对 ES6 的支持。
  TypeScript 的命令行工具安装方法入下:

npm install -g typescript

安装完就可以使用 tsc 命令,来编译 TypeScript 文件,生成 .js 文件。

tsc hello.ts

注意:
  TypeScript 只会进行静态检查,如果发现有错误,编译的时候就会报错。但是 TypeScript 编译的时候即使报错了,还是会生成编译结果。

2、基础

  TypeScript 中使用 : 指定变量的类型,: 的前后有没有空格都可以。(不指定变量类型不会报错。)

2.1、原始数据类型

(1)布尔值
  布尔值是最基础的数据类型,在 TypeScript 中,使用 boolean 定义布尔值类型。

let isDone: boolean = false;

注意: 使用构造函数 Boolean 创造的对象不是布尔值。

let creatByNewBoolean: boolean = new Boolean();
/* 
报错:error TS2322: Type 'Boolean' is not assignable to 
 type 'boolean'. 'boolean' is a primitive, but 'Boolean' 
is a wrapper object. Prefer using 'boolean' when possible.
*/

let creatByNewBoolean: Boolean = new Boolean();
// 正确

(2)数值
  使用 number 定义数值类型:

let num: number = 6;

(3)字符串
  使用 string 定义字符串类型:

let str: string = 'string';
let tem: string = `Hello, ${str}`;

(4)空值
  JavaScript 中没有空值(void)的概念,在 TypeScript 中,可以用 void 表示没有任何返回值的函数。

function alerName(): void{}

  声明一个 void 类型的变量无意义,因为它只能被赋值为 undefined 和 null。
(5)Null 和 undefined
  在 TypeScript 中,可以使用 null 和 undefined 来定义这两个原始类型的数据。

let u: undefined = undefined;
let n: null = null;

undefined 类型的变量只能被赋值为 undefined, null 类型的变量只能被赋值为 null。
注意:
  与 void 不同,undefined 和 null 是所有类型的子类型。也就是说 undefined 类型的变量,可以赋值给其他类型的变量。

let num1: number = undefined;
let num2: number = null;
// 不会报错。

let num3: number = void;
// 报错
2.2、任意值

  如果是一个普通类型,在赋值过程中不允许改变类型,会报错。但是 any 类型,则允许被赋值为任意类型。

let anyType: any = 'seven';
anyType = 7;

  在任意值上访问任何属性和方法都是允许的。(但是在执行编译后的 js 文件会报错)

let anyThing: any = 'hello';
console.log(anyThing.name);
anyThing.setName('XX");

  变量如果在声明的时候,未指定类型,那么它会被识别为任意值类型。

// 不会报错
let someThing;
someThing = 1;
someThing = 'one';

// 不同于以下代码,以下代码会报错
let s = 1;
s = 'one'; 
2.3、类型推论

  如果没有明确指定类型,那么 TypeScript 会依照类型推论的规则推断出一个类型。

let myDate = 'seven';
=> 等价于 let myDate: string = 'seven';

  在 TypeScript 2.1 之前,如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型,而且完全不被检查类型。
  在 TypeScript 2.1 中,变量类型为最后一次赋值类型。

2.4、联合类型

  联合类型表示取值可以为多种类型中的一种。联合类型使用 | 分隔每个类型。

let myDate: string | number;       

  当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,只能访问此联合类型的所有类型里面共有的属性或方法。

let mydate: string | number;
mydate.length; // 报错 Length 不是 string 和 number 的共有属性。   

联合类型的变量在被赋值的时候,会根据类型推断的规则推断出一个类型,此时使用该类型的方法不会报错。

mydate = 'seven';
console.log(mydate.length); // 5
mydate = 7;
console.log(mydate.length); // 报错。number 没有Length属性
2.5、对象的类型——接口

  在 TypeScript 中,使用接口来定义对象的类型,并且定义的变量必须和接口要保持一致。

interface Person{
    name: string;
    age: number;
}
let xl: Person = { // 必须和接口保持一致
    name: 'xl';
    age: 25;
}
let error1: Person = { // 报错,比接口少了一个属性
    name: 'e1';
}
let error2: Person = { // 报错,比接口多了一个属性
    name: 'e2';
    age: 23;
    website: 'ss'
}

  可以使用 ? 定义可选属性。可选属性可以在变量中不实现,但是在变量中不能添加新的属性。

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

  可以使用 [propName: type] 定义任意属性。

interface Person{
    name: string;
    age: number;
    [propName: string]: string;
}

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

let x: Person = {
    name: 'x';
    age: 25;
    website: 'ss';
}

任意属性的值允许是 string,但是 age 却是 number,number 不是 string 的子属性,所以报错了。
  只读属性是通过 readonly 定义。该值只能在创建的时候被赋值。

interface Person{
    readonly id: number;
}

只读属性,初始化后,在被赋值就会报错。并且只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候。

2.6、数组的类型

  在 TypeScript 中,数组类型的定义:

1、类型 + 方括号 表示法
let arr: number[] = [1,2,3];
2、数组泛型 Array<elemType> 来表示
let arr: Array<number> = [1,2,3];
3、用接口表示数组
interface NumberArray{
    [index: number]: number;
}
let arr: NumberArray = [1,2,3];
4、any 
let list: any[] = ['x',2,{}];
5、类数组
function sum() {
    let args: number[] = arguments;
}
2.7、函数的类型

  (1)一个函数的输入和输出,要在 TypeScript 中对其进行约束。

function sum(x: number, y: number): number{}
let sum = function(x:number, y:number): number{} // 通过类型推论来判断 sum 的类型
let sum: (x: number, y: number) => number = 
    function(x:number, y:number): number{}

注意: 此时,调用的时候参数要与声明的一致。

sum(1); // 报错
sum(1,2,3); // 报错

  (2)接口中函数的定义

interface Func{
    (source: string, subString: sting): boolean;
}
let func: Func;
func = function(source: string, subString: sting){
    return true;
}

  (3)使用 ? 表示可选的参数。可选参数后面不允许在出现必须参数。

function build(f: string, l?: string){}
function build(f?: string, l: string){} // 报错

  (4)参数默认值

function build(f: string, l: string = 's'){}

  (5)剩余参数

function build(array, ...items){}

  (6)允许函数重载

function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string{}
2.8、类型断言

  类型断言可以用来绕过编译器的类型推断,手动指定一个值的类型。

语法
1、<类型>值
2、值 as 类型

类型断言不是类型转换,断言成一个联合类型中不存在的类型是不允许的。

function getLength(some: string | number): number{
    // <boolean>some 报错
    if((<string>some).length){
        return (<string>some).length;
    }else{
        return some.toString().length;
    }
}
2.9、声明文件

  当使用第三方库时,我们需要引用它的声明文件。使用 declare 关键字来定义类型,帮助 TypeScript 判断传入的参数类型对不对。该定义只用于编译时检查,编译结果中会被删除。

declare var jQuery: (string) => any;
jQuery('#foo');

通常使用单独的文件声明,然后引入。

// jQuery.d.ts
declare var jQuery: (string) => any;
用 三斜线指令(///)表示引用了声明文件
/// <reference path="./jQuery.d.ts"
jQuery('#foo');
2.10、内置对象

  TypeScript 包含了 JavaScript 的内置对象,但是不包含 nodejs 的内置对象,使用 nodejs 的内置对象时需要引入第三方声明文件:

npm install @types/node --save-dev

3、进阶

3.1、类型别名

  可以使用 type 为类型起个别名。

type Name = string;
type NameResolver = () => string;
3.2、字符串字面量类型

  字符串字面量类型用来约束取值只能是某几个字符串中的一个。

type EventNames = 'click' | 'scroll' | 'mousemove'
EventNames = 'dbclick' // 报错

3.3、元组

  数组合并了相同类型的对象,元组合并了不同类型的对象。

let s: [string, number] = ['s', 22];
let x: [string, numner];
x[0] = 'xx';
x[1] = ss;
x[0].sclice(1);
或只赋值其中一项。

但当直接对元组类型的变量进行初始化或者赋值的时候,需要提供所有元组类型中指定的项。
x = [‘ss’]; // 报错
let a: [string, number] = [‘ss’] // 报错
当赋值给越界的元素时,它类型会被限制为元组中每个类型的联合类型。

x = ['s', 22, 'aaa']; 

数组第三项满足联合类型 string | number。

3.4、枚举

  枚举使用 enum 关键字来定义,枚举成员会被赋值从 0 开始递增的数组。同时也会对枚举值到枚举名进行反向映射:

enum Days {Sun, Mon};
Days['Sun'] === 0; // true
Days[0] === 'Sun'; // true

  也可以使用手动赋值。此时,未手动赋值的枚举项会接着上一个枚举项递增。需要注意的是,未手动赋值的枚举项与手动赋值重复时, TypeScript 不会报错,但是会覆盖之前的定义:

enum Days {Sun = 3,Mon = 1, Tue, Wed}
Days['Sun'] === 3; // true
Days['Mon'] === 1; // true
Days['Tue'] === 2; // true
Days['Web'] === 3; // true
Days[3] === 'Sun'; // false
Days[3] === 'Web'; // true

  手动赋值的枚举可以不是数字,此时需要使用类型断言来让 tsc 无视类型检查(编译出的 js 仍然是可用的):

enum Days {Sun = <any>"s"}

  手动赋值的枚举也可以为小数或负数,此后未手动赋值的项的递增步长仍为 1。
  常数枚举是使用 const enum 定义的枚举类型,它会在编译阶段被删除,并且不能包含计算成员:

const enum Dir{up, down};
let dir = [Dir.up, Dir.down];
编译结果:
var dir = [0, 1];

  外部枚举是使用 declare enum 来定义的枚举类型:

declare enum Dir{up, down}
let dir = [Dir.up, Dir.down];
编译结果
var dir = [Dir.up, Dir.down];
3.5、类

  TypeScript 除了实现了所有 ES6 中的类的功能外,还添加了一些新的用法。
  TypeScript 中类的用法。
(1)public private 和 protected
  TypeScript 可以使用三种访问修饰符,分别是 public private 和 protected。

  • public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有属性都是 public 的。
  • private 修饰的属性或方法是私有的,不能再声明它的类的外部访问。
  • protected 修饰的属性或方法是受保护的,它和 private 类型,区别是它在子类中也是允许被访问的。

(2)抽象类
  abstract 用于定义抽象类和其中的抽象方法。抽象类是不允许被实例化的。并且,抽象类中的抽象方法,必须被子类实现。

abstract class Animal{
    public name;
    public constrictor(name){
        this.name = name;
    }
    public abstract sayHi();
}
class Cat extends Animal{
    public sayHi(){
        console.log(`${this.name}`);
    }
}
let cat = new Cat('Tom);

(3)类的类型
  给类加上 TypeScript 的类型很简单,与接口类似:

class Person{
    name: string;
    constructor(name: string){
        this.name = name;
    }
    sayHi(): string{
        return `${this.name}`
    }
}
let a: Person = new Person('Jack');
a.sayHi(); => Jack
3.6、类与接口
  • 类至多继承(extends)一个类,和实现(implements)多个接口。
  • 接口与接口之间可以是继承关系。
  • 接口可以继承类
  • 混合类型:一个函数可以有自己属性和方法。

    interface Couter{
    (start: number): number;
    interval: number;
    reset(): void;
    }
    function getCounter(): Counter{
    let counter = function(start: number){};
    counter.interval = 123;
    counter.reset = function(){}
    return counter;
    }
    let c = getCounter();
    c(10);
    c.reset();
    c.interval = 5;

3.7、泛型

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

function createArray<T>(length: number, value: T): Array<t>{}
createArray<string>(3, 'x');
或 createArray(3, 'x');

  (2)多个类型的参数。

function swap<T, U>(typle: [T, U]): [U, T]{}
swap([1, 's']);

  (3)泛型约束,在函数内部使用泛型变量的时候,由于事先不知道它是哪种类型,所以不能随意操作它的属性或方法。这时可以对泛型进行约束。

interface Lengthwise{
    length: number;
}
function logging<T extends Lengthwise>(arg: T): T{
    arg.length;
}

调用的时候,传入的参数如果不包含 Length 将会报错。
  (4)多个类型参数之间可以相互约束。
  (5)可以使用含有泛型的接口来定义一个函数需要符合的形状。

interface Func{
    <T>(length: number, value: T): Array<T>;
}
let fun: Func;
=> 等价于
interface Func<T>{
    (length: number, value: T): Array<T>;
}
let fun: Func<any>;
fun = function<T>(length: number, value: T): Array<T>{}
fun(3, 'x');
3.8、声明合并

(1)函数的合并
  我们可以使用重载定义多个函数类型:

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('');
  }
}

(2)接口的合并
  接口中的属性在合并时会简单的合并到一个接口中:

interface Alarm {
  price: number;
}
interface Alarm {
  weight: number;
}
相当于:

interface Alarm {
  price: number;
  weight: number;
}

注意:合并的属性的类型必须是唯一的。

参考TypeScript 入门教程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值