TypeScripe学习

原始数据类型

原始数据类型包括:布尔值、数值、字符串、nullundefined 以及 ES6 中的新类型 Symbol

在TS中,使用 : 指定变量的类型

1.布尔值

let isDone: boolean = false;

事实上 new Boolean() 返回的是一个 Boolean 对象:

let createdByNewBoolean: Boolean = new Boolean(1);

直接调用 Boolean 也可以返回一个 boolean 类型

let createdByBoolean: boolean = Boolean(1);

错误的写法

let createdByNewBoolean: boolean = new Boolean(1);//错误写法
let createdByNewBoolean: Boolean = new Boolean(1);//正确写法

2.数值

let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
// ES6 中的二进制表示法
let binaryLiteral: number = 0b1010;
// ES6 中的八进制表示法
let octalLiteral: number = 0o744;
let notANumber: number = NaN;
let infinityNumber: number = Infinity;    

编译结果:

var decLiteral = 6;
var hexLiteral = 0xf00d;
// ES6 中的二进制表示法
var binaryLiteral = 10;
// ES6 中的八进制表示法
var octalLiteral = 484;
var notANumber = NaN;
var infinityNumber = Infinity;

3.字符串

let myName: string = 'Tom';
let myAge: number = 25;

// 模板字符串
let sentence: string = `Hello, my name is ${myName}.I'll be ${myAge + 1} years old next month.`;

编译结果:

var myName = 'Tom';
var myAge = 25;
// 模板字符串
var sentence = "Hello, my name is " + myName + ".
I'll be " + (myAge + 1) + " years old next month.";

4.null和undefined

null和undefined在typescript中分别是两个独立的类型限定:

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

undefined限定的变量只能赋值undefined,null也一样。

void 的区别是,undefinednull 是所有类型的子类型。也就是说 undefined 类型的变量,可以赋值给 number 类型的变量

5.空值void

void限定的变量只能赋值null或者undefined

let unusable: void = undefined;

与null和undefined限定不同的是,null和undefined是所有类型的子类型:

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

而void类型的变量不能互相随意传递:

let noContent: void;
let num: number = noContent; // 报错

 

任意值 any

允许赋值为任意类型

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

声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值

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

类型推论

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

let myFavoriteNumber = 'seven';
myFavoriteNumber = 7;

// 等价于下边
let myFavoriteNumber: string = 'seven';
myFavoriteNumber = 7;

TypeScript 会在没有明确的指定类型的时候推测出一个类型,这就是类型推论。

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

let myFavoriteNumber;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;

联合类型

表示取值可以为多种类型中的一种

联合类型使用 | 分隔每个类型

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

对象的类型——接口

在 TypeScript 中,我们使用接口来定义对象的类型

在面向对象语言中,接口是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类去实现。

1.简单的例子

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

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

上面的例子中,我们定义了一个接口 Person,接着定义了一个变量 tom,它的类型是 Person。这样,我们就约束了 tom 的形状必须和接口 Person 一致。

定义的变量不允许多一个属性或者少一个属性。

赋值的时候,变量的形状必须和接口的形状保持一致。

2.可选属性

interface Person {
    name: string;
    age?: number;  //可选属性
}

可选属性的含义是该属性可以不存在。

但是仍然不允许添加未定义的属性。

3.任意属性

interface Person {
    name: string;
    age?: number;
    [propName: string]: any;  //任意属性
}

let tom: Person = {
    name: 'Tom',
    gender: 'male'
};

使用 [propName: string] 定义了任意属性取 string 类型的值。

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

interface Person {
    name: string;
    age?: number;
    [propName: string]: string;//任意属性的值允许是 string
}

let tom: Person = {
    name: 'Tom',
    age: 25,
    gender: 'male'
};

上例中,任意属性的值允许是 string,但是可选属性 age 的值却是 numbernumber 不是 string 的子属性,所以报错了。

4.只读属性

interface Person {
    readonly id: number; //只读属性
    name: string;
    age?: number;
    [propName: string]: any;
}

let tom: Person = {
    id: 89757,
    name: 'Tom',
    gender: 'male'
};
//tom.id = 9527; //这里不允许给只读属性赋值,否则会报错

注意:只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候

数组的类型

1.「类型 + 方括号」表示法

let fibonacci: number[] = [1, 1, 2, 3, 5];
let fibonacci2: string[] = ['1', '1', '2', '3', '5'];
fibonacci.push('9')  //报错,fibonacci不是string类型
fibonacci2.push('9')

2.数组泛型

let fibonacci3: Array<number> = [1, 1, 2, 3, 5];
let fibonacci4: string[] = ['1', '1', '2', '3', '5'];
fibonacci3.push('9')  //报错,fibonacci3不是string类型
fibonacci4.push('9')

注意:在声明变量时,指定的变量为number,就不允许往其添加其他类型的值

3.用接口表示数组

interface NumberArray {
    [index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];

NumberArray 表示:只要索引的类型是数字时,那么值的类型必须是数字

4.any在数组中的运用

let list: any[] = ['xcatliu', 25, { website: 'abc' }];

用any表示数组中允许出现任意类型

5.类数组

...

函数的类型

1.函数声明

function sum(x: number, y: number): number {
    return x + y;
}

注意:输入多余或者少于的参数,是不允许的。

2.函数表达式

let mySum = function (x: number, y: number): number {
    return x + y;
};

如果我们手动给sum添加类型,则应该是:

let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
    return x + y;
};

注意:此处的=>符号和ES6中的=>符号不一样。在 TypeScript 的类型定义中,=> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。在 ES6 中,=> 叫做箭头函数。

3.用接口定义函数的形状

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

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

4.可选参数

与接口中的可选属性类似,我们用 ? 表示可选的参数

function buildName(firstName: string, lastName?: string) {
    if (lastName) {
        return firstName + ' ' + lastName;
    } else {
        return firstName;
    }
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');

注意:可选参数必须接在必须参数后面,就是说可选参数后面不允许出现必须参数

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

5.参数默认值

TypeScript 会将添加了默认值的参数识别为可选参数

function buildName(firstName: string, lastName: string = 'Cat') {
    return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');

此时就不受「可选参数必须接在必需参数后面」的限制了

function buildName(firstName: string = 'Tom', lastName: string) {
    return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let cat = buildName(undefined, 'Cat');

6.剩余参数

ES6 中,可以使用 ...rest 的方式获取函数中的剩余参数(rest 参数)

function push(array, ...items) {
    items.forEach(function(item) {
        array.push(item);
    });
}

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

注意:rest参数只能是最后一个参数,关于 rest 参数,可以参考 ES6 中的 rest 参数

7.重载

重载允许一个函数接受不同数量或类型的参数时,作出不同的处理。

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

上例中,我们重复定义了多次函数 reverse,前几次都是函数定义,最后一次是函数实现。

注意:TypeScript 会优先从最前面的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前面。

类型断言

用来手动指定一个值的类型

语法

<类型>值

  或

值 as 类型

 在 tsx 语法(React 的 jsx 语法的 ts 版)中必须用后一种。 

使用场景

之前有个例子当函数变量使用联合类型,如果我们在函数体中需要特定类型的方法,就需要使用类型断言,不然编译的时候就会报错:

function getLength(something: string | number): number {
    if ((<string>something).length) {
        return (<string>something).length; //不加类型断言就会报错
    } else {
        return something.toString().length;
    }
}

注意,类型断言不是类型转换,断言一个联合类型不存在的类型会报错:

function toBoolean(something: string | number): boolean {
    return <boolean>something; // 报错, 布尔类型不能赋值给something
}

内置对象

参考地址:https://ts.xcatliu.com/basics/built-in-objects

类型别名

用来给一个类型起个新名字

type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
    if (typeof n === 'string') {
        return n;
    } else {
        return n();
    }
}
console.log(getName('123456'))    //123456

上例中,我们使用 type 创建类型别名。

类型别名常用于联合类型。

 

字符串字面量类型

用来约束取值只能是某几个字符串中的一个

type EventNames = 'click' | 'scroll' | 'mousemove';
function handleEvent(ele: Element, event: EventNames) {
    // do something
}

handleEvent(document.getElementById('hello'), 'scroll');  // 没问题
handleEvent(document.getElementById('world'), 'dbclick'); // 报错,event 不能为 'dbclick',只能为EventNames中的其中一个

注意:类型别名与字符串字面量类型都是使用 type 进行定义。

 

元组

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

1.例子


let tom: [string, number] = ['Tom', 25];
//或者 let tom2: [string, number]; tom2[0] = 'Tom'; tom2[1] = 25; tom2[0].slice(1); tom2[1].toFixed(2);
//也可以只赋值其中一项 let tom3: [string, number]; tom3 = ['Tom', 25];

2.越界的元素

当添加越界的元素时,它的类型会被限制为元组中每个类型的联合类型

let tom5: [string, number];
tom5 = ['Tom', 25];
tom5.push('male');
// tom5.push(true); //报错,只能添加上面声明的类型

枚举

枚举类型用于取值被限定在一定范围内的场景,比如一周只能有七天,颜色限定为红绿蓝等。

1.简单的例子

枚举使用 enum 关键字来定义

枚举成员会被赋值为从 0 开始递增的数字,同时也会对枚举值到枚举名进行反向映射:

enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};

console.log(Days["Sun"] === 0); // true
console.log(Days["Mon"] === 1); // true
console.log(Days["Tue"] === 2); // true
console.log(Days["Sat"] === 6); // true

console.log(Days[0] === "Sun"); // true
console.log(Days[1] === "Mon"); // true
console.log(Days[2] === "Tue"); // true
console.log(Days[6] === "Sat"); // true

//编译成
var Days;
(function (Days) {
    Days[Days["Sun"] = 0] = "Sun";
    Days[Days["Mon"] = 1] = "Mon";
    Days[Days["Tue"] = 2] = "Tue";
    Days[Days["Wed"] = 3] = "Wed";
    Days[Days["Thu"] = 4] = "Thu";
    Days[Days["Fri"] = 5] = "Fri";
    Days[Days["Sat"] = 6] = "Sat";
})(Days || (Days = {}));

2.手动赋值

enum Days {Sun = 7, Mon = 1, Tue, Wed, Thu, Fri, Sat};

console.log(Days["Sun"] === 7); // true
console.log(Days["Mon"] === 1); // true
console.log(Days["Tue"] === 2); // true
console.log(Days["Sat"] === 6); // true

未手动赋值的枚举项会接着上一个枚举项递增

如果未手动赋值的枚举项与手动赋值的重复了,TypeScript 是不会察觉到这一点的:

enum Days {Sun = 3, Mon = 1, Tue, Wed, Thu, Fri, Sat};

console.log(Days["Sun"] === 3); // true
console.log(Days["Wed"] === 3); // true
console.log(Days[3] === "Sun"); // false
console.log(Days[3] === "Wed"); // true

上面的例子中,递增到 3 的时候与前面的 Sun 的取值重复了,但是 TypeScript 并没有报错,导致 Days[3] 的值先是 "Sun",而后又被 "Wed" 覆盖了。编译的结果是:

var Days;
(function (Days) {
    Days[Days["Sun"] = 3] = "Sun";
    Days[Days["Mon"] = 1] = "Mon";
    Days[Days["Tue"] = 2] = "Tue";
    Days[Days["Wed"] = 3] = "Wed";
    Days[Days["Thu"] = 4] = "Thu";
    Days[Days["Fri"] = 5] = "Fri";
    Days[Days["Sat"] = 6] = "Sat";
})(Days || (Days = {}));

所以使用的时候需要注意,最好不要出现这种覆盖的情况。

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

enum Days {Sun = 7, Mon, Tue, Wed, Thu, Fri, Sat = <any>"S"};

//编译成
var Days;
(function (Days) {
    Days[Days["Sun"] = 7] = "Sun";
    Days[Days["Mon"] = 8] = "Mon";
    Days[Days["Tue"] = 9] = "Tue";
    Days[Days["Wed"] = 10] = "Wed";
    Days[Days["Thu"] = 11] = "Thu";
    Days[Days["Fri"] = 12] = "Fri";
    Days[Days["Sat"] = "S"] = "Sat";
})(Days || (Days = {}));

手动赋值的枚举项也可以为小数或负数,此时后续未手动赋值的项的递增步长仍为 1。

enum Days {Sun = 7, Mon = 1.5, Tue, Wed, Thu, Fri, Sat};

console.log(Days["Sun"] === 7); // true
console.log(Days["Mon"] === 1.5); // true
console.log(Days["Tue"] === 2.5); // true
console.log(Days["Sat"] === 6.5); // true

3.常数项和计算所得项

枚举项有两种类型:常数项和计算所得项。

前面我们所举的例子都是常数项,一个典型的计算所得项的例子:

enum Color {Red, Green, Blue = "blue".length};

上面的例子中,"blue".length 就是一个计算所得项。

上面的例子不会报错,但是如果紧接在计算所得项后面的是未手动赋值的项,那么它就会因为无法获得初始值而报错

enum Color {Red = "red".length, Green, Blue};

// index.ts(1,33): error TS1061: Enum member must have initializer.
// index.ts(1,40): error TS1061: Enum member must have initializer.

4.常数枚举

使用 const enum 定义的枚举类型

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

let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];   //打印结果为[0,1,2,3]

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

假如包含了计算成员,则会在编译阶段报错:

const enum Color {Red, Green, Blue = "blue".length};

// index.ts(1,38): error TS2474: In 'const' enum declarations member initializer must be constant expression

5.外部枚举

使用 declare enum 定义的枚举类型

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

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

之前提到过,declare 定义的类型只会用于编译时的检查,编译结果中会被删除。

上例的编译结果是:

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

外部枚举与声明语句一样,常出现在声明文件中。

同时使用 declareconst 也是可以的:

declare 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 */];

TypeScript 的枚举类型的概念来源于 C#

 

 

原文地址:https://www.cnblogs.com/chensv/p/12202052.html