TypeScript
什么是TypeScript
- JavaScript超集
- 支持ECMAScript6标准,并支持输出ECMAScript 3/5
JavaScript 与 TypeScript 的区别
TypeScript 是 JavaScript 的超集,扩展了 JavaScript 的语法,因此现有的 JavaScript 代码可与 TypeScript 一起工作无需任何修改,TypeScript 通过类型注解提供编译时的静态类型检查
安装typescript
npm install -g typescript
tsc app.ts
或者在线typescript编译网站:
基础数据类型
- 布尔类型
let bool: boolean = false;
// js
let bool = false;
- 数字
let num: number = 6
// js
let num = 6
- 字符串
let str: string = 'string'
// js
let str = 'string'
- 数组
let list: number[] = [1, 2, 3];
// js
let list = [1, 2, 3];
- 元组 Tuple
let x: [string, number] = ['hello', 6]
// js
let x = ['hello', 6];
- 枚举 enum
enum Direction {
NORTH
SOUTH
EAST
WEST
}
// js
var Direction;
(function (Direction) {
Direction[Direction["NORTH"] = 0] = "NORTH";
Direction[Direction["SOUTH"] = 1] = "SOUTH";
Direction[Direction["EAST"] = 2] = "EAST";
Direction[Direction["WEST"] = 3] = "WEST";
})(Direction || (Direction = {}));
- any
let notSure: any = 4;
- void
用于标识方法返回值的类型,表示该方法没有返回值。
function hello(): void {
alert("Hello Runoob");
}
// js
function hello() {
alert("Hello Runoob");
}
- null
表示对象值缺失。
let n: null = null;
- undefined
let u: undefined = undefined;
默认情况下null
和undefined
是所有类型的子类型。 就是说你可以把 null
和undefined
赋值给number
类型的变量。然而,当你指定了--strictNullChecks
标记,null
和undefined
只能赋值给void
和它们各自。
- never
never
类型表示的是那些永不存在的值的类型。 例如, 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) {
}
}
- Object
object
表示非原始类型,也就是除number
,string
,boolean
,symbol
,null
或undefined
之外的类型。
使用object
类型,就可以更好的表示像Object.create
这样的API。例如:
declare function create(o: object | null): void;
create({ prop: 0 }); // OK
create(null); // OK
TypeScript断言
类型断言有2种形式
- “尖括号” 语法
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;
类型守卫
类型保护是可执行运行时检查的一种表达式,用于确保该类型在一定的范围内。换句话说,类型保护可以保证一个字符串是一个字符串,尽管它的值也可以是一个数值。类型保护与特性检测并不是完全不同,其主要思想是尝试检测属性、方法或原型,以确定如何处理值。目前主要有四种的方式来实现类型保护:
- 属性判断: in
interface Foo {
foo: string;
}
interface Bar {
bar: string;
}
function test(input: Foo | Bar) {
if('foo' in input) {
// 这里 input 的类型「收紧」为 Foo
} else {
// 这里 input 的类型「收紧」为 Bar
}
}
- 类型判断: typeof
function test(input: string | number) {
if(typeof input == 'string') {
// 这里 input 的类型「收紧」为 string
} else {
// 这里 input 的类型「收紧」为 number
}
}
- 实例判断: instanceof
class Foo {};
class Bar {};
function test(input: Foo | Bar) {
if(input instanceof Foo) {
// 这里 input 的类型「收紧」为 Foo
} else {
// 这里 input 的类型「收紧」为 Bar
}
}
- 字面量相等判断 ==, !=, ===, !==
type Foo = 'foo' | 'bar' | 'unknown';
function test(input: Foo) {
if (input != 'unknown') {
// 这里 input 的类型「收紧」为 'foo' | 'bar'
} else {
// 这里 input 的类型「收紧」为 'unknown'
}
}
高级类型
1. 联合类型(Union Types)
代码库希望传入 number或 string类型的参数,可以使用 联合类型做为 padding的参数:
/**
* Takes a string and adds "padding" to the left.
* If 'padding' is a string, then 'padding' is appended to the left side.
* If 'padding' is a number, then that number of spaces is added to the left side.
*/
function padLeft(value: string, padding: string | number) {
// ...
}
let indentedString = padLeft("Hello world", true); // errors during compilation
如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员。
interface Bird {
fly();
layEggs();
}
interface Fish {
swim();
layEggs();
}
function getSmallPet(): Fish | Bird {
// ...
}
let pet = getSmallPet();
pet.layEggs(); // okay
pet.swim(); // errors
- 类型保护与区分类型
let pet = getSmallPet();
if ((pet).swim) {
(pet).swim();
}
else {
(pet).fly();
}
类型别名
类型别名用来给一个类型起个新名字。
type Message = string | string[];
let greet = (message: Message) => {
// ...
};
交叉类型
交叉类型是将多个类型合并为一个类型。 这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。
function extend(first: T, second: U): T & U {
let result = {};
for (let id in first) {
(result)[id] = (first)[id];
}
for (let id in second) {
if (!result.hasOwnProperty(id)) {
(result)[id] = (second)[id];
}
}
return result;
}
class Person {
constructor(public name: string) { }
}
interface Loggable {
log(): void;
}
class ConsoleLogger implements Loggable {
log() {
// ...
}
}
var jim = extend(new Person("Jim"), new ConsoleLogger());
var n = jim.name;
jim.log();
函数
函数定义
// Named function
function test() {
}
// Anonymous function
let test = function() {};
函数类型
function add(x: number, y: number): number {
return x + y;
}
let myAdd = function(x: number, y: number): number { return x + y; };
可选参数
编译器会校验传递给一个函数的参数个数必须与函数期望的参数个数一致。
JavaScript里,每个参数都是可选的,可传可不传。 没传参的时候,它的值就是undefined。 在TypeScript里我们可以在参数名旁使用 ?实现可选参数的功能。
function test(a: string, b?: string) {
if(b) {
return b
} else {
return a
}
}
可选参数必须跟在必须参数后面。
默认参数
function buildName(firstName: string, lastName = "Smith") {
return firstName + " " + lastName;
}
let result1 = buildName("Bob"); // works correctly now, returns "Bob Smith"
剩余参数
必要参数,默认参数和可选参数有个共同点:它们表示某一个参数。 有时,你想同时操作多个参数,或者你并不知道会有多少参数传递进来。 在JavaScript里,你可以使用 arguments来访问所有传入的参数。
function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
}
let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");
省略号也会在带有剩余参数的函数类型定义上使用到:
function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
}
let buildNameFun: (fname: string, ...rest: string[]) => string = buildName;
重载
数组
数据解构
let x: number; let y: number; let z: number;
let five_array = [0,1,2,3,4];
[x,y,z] = five_array;
数组展开运算符
let two_array = [0, 1];
let five_array = [...two_array, 2, 3, 4];
数组遍历
接口
接口
interface IQuery {
page: number;
findOne(): void;
findAll(): void;
}
可选属性
interface SquareConfig {
color?: string;
width?: number;
}
只读属性
interface Point {
readonly x: number;
readonly y: number;
}
readonly与const区别
最简单判断该用readonly
还是const
的方法是看要把它做为变量使用还是做为一个属性。 做为变量使用的话用 const
,若做为属性则使用readonly
。
额外的属性检查
let mySquare = createSquare({ width: 100, opacity: 0.5 } as SquareConfig);
或者
interface SquareConfig {
color?: string;
width?: number;
[propName: string]: any;
}
函数类型
除了用接口描述对象结构外,接口也可以描述函数类型。
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
let result = source.search(subString);
return result > -1;
}
泛型
软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
function identity(arg: T): T {
return arg;
}
使用方式
- 第一种 传入所有的参数,包含类型参数
let output = identity("myString"); // type of output will be 'string'
- 第二种 类型推论 -- 即编译器会根据传入的参数自动地帮助我们确定T的类型
let output = identity("myString"); // type of output will be 'string'
泛型类
class GenericNumber {
zeroValue: T;
add: (x: T, y: T) => T;
}
泛型约束
interface Lengthwise {
length: number;
}
function loggingIdentity(arg: T): T {
console.log(arg.length); // Now we know it has a .length property, so no more error
return arg;
}
迭代器与生成器
可迭代性
当一个对象实现了Symbol.iterator
属性时,我们认为它是可迭代的。 一些内置的类型如 Array
,Map
,Set
,String
,Int32Array
,Uint32Array
等都已经实现了各自的Symbol.iterator
。 对象上的 Symbol.iterator
函数负责返回供迭代的值。