类型推论
如果没有明确指定类型,typescript会根据类型推论的规则推断出一个类型。
注意:
如果定义的时候没有赋值,不管之后有没有赋值,都会被推断为any
类型而完全不被类型检查
联合类型
联合类型表示取值可以为多种类型中的一种。
使用|
来分隔类型
示例代码:
let myFavoriteNumber: string | number;
注意:
当不确定一个联合类型的变量到底是哪个类型的时候,只能访问此联合类型的所有类型共有的属性和方法。
接口
- 赋值的时候,变量的形状必须和接口的形状保持一致
- 可选属性使用
?
- 任意属性:使用
[propName: string]
定义了任意属性取 string 类型的值。
interface Person {
name: string;
age?: number;
[propName: string]: any;
}
注意:
- 定义来任意属性后,确定属性和可选属性的类型必须是任意属性类型的子集
- 如果同时存在任意属性、可选属性,那么任意属性的数据类型要带undefined
- 只读属性
readonly
注意: 只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候:
数组的类型
类型[]
表示法
let fibonacci: number[] = [1, 1, 2, 3, 5];
- 可以使用数组泛型来表示数组
let fibonacci: Array<number> = [1, 1, 2, 3, 5];
- 用接口表示数组(过于复杂,一般不用)
interface NumberArray {
[index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];
NumberArray
表示:只要索引的类型是数字时,那么值的类型必须是数字
可以用接口来描述类数组:
function sum() {
let args: {
[index: number]: number;
length: number;
callee: Function;
} = arguments;
}
数组里any
的使用
表示数组中允许出现任意类型
let list: any[] = ['xcatliu', 25, { website: 'http://xcatliu.com' }];
函数的类型
两种常见的定义函数的方式:
- 函数声明
function sum(x: number, y: number): number {
return x + y;
}
- 函数表达式
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
};
注意:
在 TypeScript
的类型定义中,=>
用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。
在 ES6 中,=>
叫做箭头函数.
用接口定义函数的形状
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
return source.search(subString) !== -1;
}
关于函数的参数:
- 可选参数后面不允许再出现必需参数
- 添加了默认值的参数会被识别为可选参数,不受前面☝️第一条限制
示例代码:
function buildName(firstName: string = 'Tom', lastName: string, nickName?: string) {
return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let cat = buildName(undefined, 'Cat', 'mimi');
重载:
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('');
}
}
注意:
TypeScript
会优先从最前面的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前面。
类型断言
类型断言可以用来手动指定一个值
使用语法:
值 as 类型
(建议使用as语法,不容易混淆)或 <类型>值
注意: 类型断言只能够「欺骗」TypeScript
编译器,无法避免运行时的错误。小心使用!
使用场景:
判断传入的参数是不是ApiError
类型
interface ApiError extends Error {
code: number;
}
interface HttpError extends Error {
statusCode: number;
}
function isApiError(error: Error) {
if (typeof (error as ApiError).code === 'number') {
return true;
}
return false;
}
如上,因为接口是一个类型,不是一个真正的值,它在编译结果中会被删除,就无法使用 instanceof
来做运行时判断,此时就只能使用类型断言来判断。
将类型断言为any
window.foo = 1;
上述代码,在TS编译时会报错,显示window上不存在foo属性
将window断言为any类型,则可以运行:
(window as any).foo = 1;
在any
类型的变量上,访问任何属性都是允许的。
注意: 将类型断言为any
,是我们解决问题的最后一个手段。(在类型的严格性和开发的便利性之间掌握平衡)
类型断言总结:
- 联合类型可以被断言为其中一个类型
- 父类可以被断言为子类
- 任何类型都可以被断言为any
- any可以被断言为任何类型
由第3,4点,产生了双重断言,形式如as any as Foo
, 了解了解,不要使用
注意:
- 类型断言只会影响TS编译时的类型,类型断言语句在编译结果中会被删除。它不会影响到变量的类型,若要进行类型转换,直接使用类型转换方法。
- 类型声明比类型断言更加严格(优先使用类型声明)
const tom = getCacheData('tom') as Cat; // 类型断言
const tom: Cat = getCacheData('tom'); // 类型声明
- 可以使用泛型来更加规范的实现对类型的约束
声明文件
declare var
声明全局变量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
扩展模块/// <reference />
三斜线指令
内置对象
- ECMAScript标准提供的内置对象有:
Boolean
Error
Date
RegExp
等 - DOM和BOM提供的内置对象有:
Document
,HTMLElement
Event
NodeList
等 - TypeScript 核心库的定义文件中定义了所有浏览器环境需要用到的类型,并且是预置在 TypeScript 中的。
注意: TypeScript 核心库的定义中不包含 Node.js 部分。想要使用,需要引入第三方声明文件
npm install @types/node --save-dev
类型别名
类型别名用来给一个类型起个新名字,常用于联合类型。
type Name = string;
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';
function handleEvent(ele: Element, event: EventNames) {
// do something
}
handleEvent(document.getElementById('hello'), 'scroll'); // 没问题
handleEvent(document.getElementById('world'), 'dblclick'); // 报错,event 不能为 'dblclick'
// index.ts(7,47): error TS2345: Argument of type '"dblclick"' is not assignable to parameter of type 'EventNames'.
注意: 类型别名与字符串字面量类型都是使用 type 进行定义。