参考:typescript.bootcss.com/basic-types… ts.xcatliu.com/basics/type…
基础类型:
1.布尔值:boolean
let isDone: boolean = false;
2.数字:number
let decLiteral: number = 6; let hexLiteral: number = 0xf00d;
3.字符串: string
let name: string = "bob";
name = "smith";
它可以定义多行文本和内嵌表达式。这种字符串是被反引号包围(),并且以${ expr }这种形式嵌入表达式 let name: string = Gene; let sentence: string = Hello, my name is ${ name }.
4.数组:[]
let list: number[] = [1, 2, 3]; 第二种方式是使用数组泛型,Array<元素类型>: let list: Array = [1, 2, 3];
5.元组 Tuple
元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。比如,你可以定义一对值分别为string和number类型的元组。 let x: [string, number]; x = ['hello', 10]; // OK x = [10, 'hello']; // Error
6.枚举 enum
enum类型是对JavaScript标准数据类型的一个补充。像C#等其它语言一样,使用枚举类型可以为一组数值赋予友好的名字。 enum Color {Red, Green, Blue} let c: Color = Color.Green; 默认情况下,从0开始为元素编号。你也可以手动的指定成员的数值。例如,我们将上面的例子改成从1开始编号: enum Color {Red = 1, Green = 2, Blue = 4} let colorName: string = Color[2]; alert(colorName); // 显示'Green'因为上面代码里它的值是2
7.任意值 any
有时候,我们会想要为那些在编程阶段还不清楚类型的变量指定一个类型。这些值可能来自于动态的内容,比如来自用户输入或第三方代码库。这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。那么我们可以使用any类型来标记这些变量 当你只知道一部分数据的类型时,any类型也是有用的。比如,你有一个数组,它包含了不同的类型的数据: let list: any[] = [1, true, "free"]; list[1] = 100;
8.空值 void
某种程度上来说,void类型像是与any类型相反,它表示没有任何类型。当一个函数没有返回值时,你通常会见到其返回值类型是void: function warnUser(): void { alert("This is my warning message"); } 声明一个void类型的变量没有什么大用,因为你只能为它赋予undefined和null
9.Null 和 Undefined
TypeScript里,undefined和null两者各自有自己的类型分别叫做undefined和null。和void相似,它们的本身的类型用处不是很大 默认情况下null和undefined是所有类型的子类型。就是说你可以把null和undefined赋值给number类型的变量 然而,当你指定了--strictNullChecks标记,null和undefined只能赋值给void和它们各自。这能避免很多常见的问题。也许在某处你想传入一个string或null或undefined,你可以使用联合类型string | null | undefined。再次说明,稍后我们会介绍联合类型。
10.Never
never类型表示的是那些永不存在的值的类型。例如,never类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型;变量也可能是never类型,当它们被永不为真的类型保护所约束时。 never类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是never的子类型或可以赋值给never类型(除了never本身之外)。即使any也不可以赋值给never。 下面是一些返回never类型的函数: // 返回never的函数必须存在无法达到的终点 function error(message: string): never { throw new Error(message); } // 推断的返回值类型为never function fail() { return error("Something failed"); } // 返回never的函数必须存在无法达到的终点 function infiniteLoop(): never { while (true) { } }
联合类型
联合类型(Union Types)表示取值可以为多种类型中的一种。 let myFavoriteNumber: string | number; myFavoriteNumber = 'seven'; myFavoriteNumber = 7;
###类型断言 有时候你会遇到这样的情况,你会比TypeScript更了解某个值的详细信息。 通常这会发生在你清楚地知道一个实体具有比它现有类型更确切的类型。 类型断言有两种形式。其一是“尖括号”语法: let someValue: any = "this is a string"; let strLength: number = (someValue).length; 另一个为as语法: let someValue: any = "this is a string"; let strLength: number = (someValue as string).length; 两种形式是等价的。至于使用哪个大多数情况下是凭个人喜好;然而,当你在TypeScript里使用JSX时,只有as语法断言是被允许的。
var let const
你可能已经注意到了,我们使用let关键字来代替大家所熟悉的JavaScript关键字var。 let关键字是JavaScript的一个新概念,TypeScript实现了它。 我们会在以后详细介绍它,很多常见的问题都可以通过使用let来解决,所以尽可能地使用let来代替var吧。
ts中不采用var 存在缺陷问题 let vs const 拥有相同的作用域(块级)const被赋值后不能再改变 大部分都用let 简洁性
解构
解构数组
最简单的解构莫过于数组的解构赋值了: let input = [1, 2]; let [first, second] = input; console.log(first); // outputs 1 console.log(second); // outputs 2 你可以在数组里使用...语法创建剩余变量: let [first, ...rest] = [1, 2, 3, 4]; console.log(first); // outputs 1 console.log(rest); // outputs [ 2, 3, 4 ]
对象解构
你也可以解构对象: let o = { a: "foo", b: 12, c: "bar" }; let { a, b } = o; 这通过 o.a and o.b 创建了 a 和 b 。 注意,如果你不需要 c 你可以忽略它。 你可以在对象里使用...语法创建剩余变量: let { a, ...passthrough } = o; let total = passthrough.b + passthrough.c.length;
展开
展开操作符正与解构相反。 它允许你将一个数组展开为另一个数组,或将一个对象展开为另一个对象。 例如: let first = [1, 2]; let second = [3, 4]; let bothPlus = [0, ...first, ...second, 5]; 这会令bothPlus的值为[0, 1, 2, 3, 4, 5]。 展开操作创建了first和second的一份浅拷贝。 它们不会被展开操作所改变。 你还可以展开对象: let defaults = { food: "spicy", price: "", ambiance: "noisy" }; let search = { ...defaults, food: "rich" }; search的值为{ food: "rich", price: "", ambiance: "noisy" }。 对象的展开比数组的展开要复杂的多。 像数组展开一样,它是从左至右进行处理,但结果仍为对象。 这就意味着出现在展开对象后面的属性会覆盖前面的属性。
接口(interface)
介绍
TypeScript的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。
接口初探
interface LabelledValue { label: string; } function printLabel(labelledObj: LabelledValue) { console.log(labelledObj.label); } let myObj = {size: 10, label: "Size 10 Object"}; printLabel(myObj); LabelledValue接口就好比一个名字,用来描述上面例子里的要求。 它代表了有一个label属性且类型为string的对象。 需要注意的是,我们在这里并不能像在其它语言里一样,说传给printLabel的对象实现了这个接口。我们只会去关注值的外形。 只要传入的对象满足上面提到的必要条件,那么它就是被允许的。 还有一点值得提的是,类型检查器不会去检查属性的顺序,只要相应的属性存在并且类型也是对的就可以。
可选属性
可选属性名字定义的后面加一个?符号
只读属性
可选属性名字定义的前面加一个readonly
任意属性
使用 [propName: string] 定义了任意属性取 string 类型的值。[propName: string]: any;
继承接口
和类一样,接口也可以相互继承。 interface Shape { color: string; } interface PenStroke { penWidth: number; } interface Square extends Shape, PenStroke { sideLength: number; } let square = {}; square.color = "blue"; square.sideLength = 10; square.penWidth = 5.0;
声明文件
ts.xcatliu.com/basics/decl… 当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。 ● 声明文件必需以 .d.ts 为后缀。 一般来说,ts 会解析项目中所有的 *.ts 文件,当然也包含以 .d.ts 结尾的文件。 ○ declare var 声明全局变量 (declare let 和 declare const) ○ declare function 声明全局方法 ○ declare class 声明全局类 ○ declare enum 声明全局枚举类型 ○ declare namespace 声明(含有子属性的)全局对象 ○ interface 和 type 声明全局类型 ○ export 导出变量 ○ export namespace 导出(含有子属性的)对象 ○ export default ES6 默认导出 ○ export = commonjs 导出模块 ○ export as namespace UMD 库声明全局变量 ○ declare global 扩展全局变量 ○ declare module 扩展模块 ○ /// 三斜线指令
函数的类型
ts.xcatliu.com/basics/type… 在 JavaScript 中,有两种常见的定义函数的方式——函数声明(Function Declaration)和函数表达式(Function Expression): // 函数声明(Function Declaration) function sum(x, y) { return x + y; } // 函数表达式(Function Expression) let mySum = function (x, y) { return x + y; } 一个函数有输入和输出,要在 TypeScript 中对其进行约束,需要把输入和输出都考虑到,其中函数声明的类型定义较简单: function sum(x: number, y: number): number { return x + y; } 注意,输入多余的(或者少于要求的)参数,是不被允许的:
可选属性
可选属性名字定义的后面加一个?符号
类 class
参考: ts.xcatliu.com/advanced/cl… 类的概念 虽然 JavaScript 中有类的概念,但是可能大多数 JavaScript 程序员并不是非常熟悉类,这里对类相关的概念做一个简单的介绍。
- 类(Class):定义了一件事物的抽象特点,包含它的属性和方法
- 对象(Object):类的实例,通过 new 生成
- 面向对象(OOP)的三大特性:封装、继承、多态
- 封装(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要(也不可能)知道细节,就能通过对外提供的接口来访问该对象,同时也保证了外界无法任意更改对象内部的数据
- 继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性
- 多态(Polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应。比如 Cat 和 Dog 都继承自 Animal,但是分别实现了自己的 eat 方法。此时针对某一个实例,我们无需了解它是 Cat 还是 Dog,就可以直接调用 eat 方法,程序会自动判断出来应该如何执行 eat
- 存取器(getter & setter):用以改变属性的读取和赋值行为
- 修饰符(Modifiers):修饰符是一些关键字,用于限定成员或类型的性质。比如 public 表示公有属性或方法
- 抽象类(Abstract Class):抽象类是供其他类继承的基类,抽象类不允许被实例化。抽象类中的抽象方法必须在子类中被实现
- 接口(Interfaces):不同类之间公有的属性或方法,可以抽象成一个接口。接口可以被类实现(implements)。一个类只能继承自另一个类,但是可以实现多个接口
ES6 中类的用法
属性和方法
使用 class 定义类,使用 constructor 定义构造函数。 通过 new 生成新实例的时候,会自动调用构造函数
类的继承
使用 extends 关键字实现继承,子类中使用 super 关键字来调用父类的构造函数和方法。
存取器
使用 getter 和 setter 可以改变属性的赋值和读取行为
静态方法
使用 static 修饰符修饰的方法称为静态方法,它们不需要实例化,而是直接通过类来调用
ES7 中类的用法
实例属性
ES6 中实例的属性只能通过构造函数中的 this.xxx 来定义,ES7 提案中可以直接在类里面定义
静态属性
ES7 提案中,可以使用 static 定义一个静态属性:
TypeScript 中类的用法
public private 和 protected
TypeScript 可以使用三种访问修饰符(Access Modifiers),分别是 public、private 和 protected。
- public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的
- private 修饰的属性或方法是私有的,不能在声明它的类的外部访问
- protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的
参数属性
修饰符和readonly还可以使用在构造函数参数中,等同于类中定义该属性同时给该属性赋值,使代码更简洁。
readonly
只读属性关键字,只允许出现在属性声明或索引签名或构造函数中。
抽象类
abstract 用于定义抽象类和其中的抽象方法。
类的类型
给类加上 TypeScript 的类型很简单,与接口类似:
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
sayHi(): string {
return My name is ${this.name};
}
}
let a: Animal = new Animal('Jack');
console.log(a.sayHi()); // My name is Jack
泛型
泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
简单的例子
首先,我们来实现一个函数 createArray,它可以创建一个指定长度的数组,同时将每一项都填充一个默认值: function createArray(length: number, value: any): Array { let result = []; for (let i = 0; i < length; i++) { result[i] = value; } return result; } createArray(3, 'x'); // ['x', 'x', 'x'] 这段代码编译不会报错,但是一个显而易见的缺陷是,它并没有准确的定义返回值的类型: Array 允许数组的每一项都为任意类型。但是我们预期的是,数组中每一项都应该是输入的 value 的类型。 这时候,泛型就派上用场了: function createArray(length: number, value: T): Array { let result: T[] = []; for (let i = 0; i < length; i++) { result[i] = value; } return result; } createArray(3, 'x'); // ['x', 'x', 'x'] 上例中,我们在函数名后添加了 ,其中 T 用来指代任意输入的类型,在后面的输入 value: T 和输出 Array 中即可使用了。 接着在调用的时候,可以指定它具体的类型为 string。当然,也可以不手动指定,而让类型推论自动推算出来。
多个类型参数
定义泛型的时候,可以一次定义多个类型参数: function swap<T, U>(tuple: [T, U]): [U, T] { return [tuple[1], tuple[0]]; } swap([7, 'seven']); // ['seven', 7]
泛型约束
在函数内部使用泛型变量的时候,由于事先不知道它是哪种类型,所以不能随意的操作它的属性或方法 function loggingIdentity(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(arg: T): T { console.log(arg.length); return arg; } 上例中,我们使用了 extends 约束了泛型 T 必须符合接口 Lengthwise 的形状,也就是必须包含 length 属性。
泛型接口
当然也可以使用含有泛型的接口来定义函数的形状 interface CreateArrayFunc { (length: number, value: T): Array; }
let createArray: CreateArrayFunc; createArray = function(length: number, value: T): Array { let result: T[] = []; for (let i = 0; i < length; i++) { result[i] = value; } return result; } createArray(3, 'x'); // ['x', 'x', 'x']
泛型类
与泛型接口类似,泛型也可以用于类的类型定义中 泛型参数的默认类型 在 TypeScript 2.3 以后,我们可以为泛型中的类型参数指定默认类型。当使用泛型时没有在代码中直接指定类型参数,从实际值参数中也无法推测出时,这个默认类型就会起作用。
扩展阅读
此处记录了官方手册(中文版)中包含,但是本书未涉及的概念。 我认为它们是一些不重要或者不属于 TypeScript 的概念,所以这里只给出一个简单的释义,详细内容可以点击链接深入理解。
- Never(中文版):永远不存在值的类型,一般用于错误处理函数
- Variable Declarations(中文版):使用 let 和 const 替代 var,这是 ES6 的知识
- this:箭头函数的运用,这是 ES6 的知识
- Using Class Types in Generics(中文版):创建工厂函数时,需要引用构造函数的类类型
- Best common type(中文版):数组的类型推论
- Contextual Type(中文版):函数输入的类型推论
- Type Compatibility(中文版):允许不严格符合类型,只需要在一定规则下兼容即可
- Advanced Types(中文版):使用 & 将多种类型的共有部分叠加成一种类型
- Type Guards and Differentiating Types(中文版):联合类型在一些情况下被识别为特定的类型
- Discriminated Unions(中文版):使用 | 联合多个接口的时候,通过一个共有的属性形成可辨识联合
- Polymorphicthistypes(中文版):父类的某个方法返回 this,当子类继承父类后,子类的实例调用此方法,返回的 this 能够被 TypeScript 正确的识别为子类的实例。
- Symbols(中文版):新原生类型,这是 ES6 的知识
- Iterators and Generators(中文版):迭代器,这是 ES6 的知识
- Namespaces(中文版):避免全局污染,现在已被 ES6 Module 替代
- Decorators(中文版):修饰器,这是 ES7 的一个提案
- Mixins(中文版):一种编程模式,与 TypeScript 没有直接关系,可以参考 ES6 中 Mixin 模式的实现