数组
基本定义
在TypeScript中,数组的定义如下:
let fibonacci: number[] = [1,2,3,4,5] 复制代码
上面的🌰中,不允许出现除number以外的类型,比如:
let fibonacci: number[] = [1,2,3, true] 复制代码
这样写会抛出异常不能将类型“(number | boolean)[]”分配给类型“number”
数组的方法也会根据数组在定义时的类型约定,受到限制,举个🌰
let fibonacci: number = [1,2,3,4] fibonacce.push(true) 复制代码
这样写也不行,会抛出错误不能将类型“number[]”分配给类型“number”
&& 不能将类型“number[]”分配给类型“number”
接口表示
举个🌰
interface NumberArray { [index: number]: number; } let fibonacce: NumberArray = [1,2,3,4] 复制代码
NumberArray
:索引是数字时,值的类型必须用数字。
一般不会用这个去定义一个数组。
类数组
类数组不能用数组定义的方式去赋值,举个🌰
function sum() { let args: number[] = arguments; } 复制代码
这样写会抛出错误类型“IArguments”缺少类型“number[]”的以下属性: pop, push, concat, join 及其他 24 项
因为类数组并没有数组原型上的方法,pop等等,所以如果用array去定义,那么类型校验不通过,我们可以用IArguments
类型去定义一个类数组,举个🌰:
function sum() { let agrs: IArguments = arguments; } 复制代码
类型IArguments
其实就是一个interface
,是TypeScript内置的类型,相当于这样写:
interface IAgruments { [index: number]: any; length: number; callee: function; } 复制代码
除此之外,TypeScript中还有很多内置的类型,比如NodeList
,HTMLCollection
等
数组 any
无限制的数组项,举个🌰
let list: any[] = [1, '1', true, {name: '1'}, [3,4,5]] 复制代码
完全ok!
函数
基本定义
TypeScript中函数的定义如下:
function sum(x: number, y: number): number { return x + y } 复制代码
函数表达式
let sum = function(x: number, y: nunmber): number { return x + y } 复制代码
sum并没有类型的定义,可以给sum也加一个定义:
let sum: (x: number, y: number) => number = function(x: number, y: number): number { return x + y } 复制代码
上面所有的定义中,函数的参数都是必传的,不能少,也不能多,比如这样:
再比如,这样:
可选参数
与接口中的可选属性类似,用?
表示,举个🌰:
let buildName: (f: string, l?: string) => string = function ( firstName: string, lastName?: string ): string { if (lastName) return firstName + " " + lastName; return firstName; }; console.log(buildName("Alice")); console.log(buildName("Alice", "Yan")); 复制代码
需要注意的是,可选参数必须在最后面,也就是说,可选参数的后面,不能再接必需参数,像这样就不行:
参数默认值
TypeScript会将添加了默认值的参数自动设置为可选参数,举个🌰
function buildName(firstName: string, lastName: string = 'Yan'): string { return firstName + ' ' + lastName } console.log(buildName('Alice')) 复制代码
此时就不受「可选参数必须在必须参数后面」的限制了
剩余参数
...rest
获取剩余参数
function push(array: any[], ...items: any[]) { items.forEach( item => array.push(item)) } 复制代码
类型断言
用于手动指定一个值的类型
基本语法
(推荐)
值 as 类型 复制代码
or
(不推荐)
<类型> 值 复制代码
用途
将一个联合类型断言为其中一个类型
TypeScript不确定一个联合类型的变量到底属于哪个类型的时候,只能访问此联合类型的所有类型中共有的属性或方法,比如之前说的string
| number
访问toString
,再举个栗子:
interface Dog { name: string; run(): void; } interface Fish { name: string; swim(): void; } function getName(animal: Dog | Fish) { return animal.name } 复制代码
有时候,我们确实需要在还不确定类型的时候就访问其中一个类型特有的属性或方法,举个栗子:
interface Dog { name: string; run(): void; } interface Fish { name: string; swim(): void; } function isFish(animal: Dog | Fish) { if( typeof animal.swim === 'function' ) return true return false } 复制代码
上面这个栗子就会抛出错误类型“Dog | Fish”上不存在属性“swim”
这个时候我们就可以用类型断言
,将animal
断言成Fish
:
interface Dog { name: string; run(): void; } interface Fish { name: string; swim(): void; } function isFish(animal: Dog | Fish): boolean { if(typeof (animal as Fish).swim === 'function') return true return false } 复制代码
📢注意: 类型断言只能够【欺骗】TypeScript编译器,无法避免运行时的错误,滥用类型断言可能会导致运行错误,举个栗子:
interface Dog { name: string; run(): void; } interface Fish { name: string; swim(): void; } function goSwim(animal: Dog | Fish): void { (animal as Fish).swim() } const tony: Dog = { name: 'Tony', run() { console.log('im run!')} } swim(tony) 复制代码
将一个父类断言为更加具体的子类
当类之间有继承关系时,类型断言也是很常见,举个栗子:
class ApiError extends Error { code: number = 0; } class HttpError extends Error { statusCode: number = 200; } function isApiError(error: Error) { if( typeof (error as ApiError).code === 'number') return true return false } 这个栗子中,声明了函数`isApiError`,用来判断传入的参数是不是`ApiError`类,但是由于父类`Error`中并没有`code`这个属性,所以直接使用就会报错,就要使用`as`进行`类型断言` 复制代码
将任何一个类型断言为any
这其实就是有一点不靠谱了,咱就是整个就是说你定义一个类型是number
,但是如果你又觉得他好像不是number
,那你可以把他再断言成any
,举个栗子:
const foo: number = 1 foo.length = 1 复制代码
这样写是不能通过编译的,因为foo
是number
类型,是没有length
属性的,所以TypeScript给了提示类型“number”上不存在属性“length”。
,这种提示非常有效!
但是有时候我们的写法是完全没有问题的,比如:
window.foo = 1 复制代码
在js中,这种写法完全ok,给window
添加属性foo
,值为1
,但是,在TypeScript中是不支持的,它会抛出这个错误类型“Window & typeof globalThis”上不存在属性“foo”。
,这时候我们就可以用类型断言,把window
断言成any
,any
类型上,访问任何属性都是允许的,像这样:
(window as any).foo = 1 复制代码
ok
将any断言成任何一种类型
举个栗子:
function getCacheData(key: string): any { return (window as any).cache[key] } 复制代码
上面的例子中,getCacheData
返回的类型是any,我们不确定他到底返回的是什么类型,所以当我们使用这个function的时候,我们可以根据自己的需要,对他的返回值进行断言,举个栗子:
interface Cat { name: string; run(): void; } const tom = getCacheData('tom') as Cat; tom.run() 复制代码
断言包含
并不是所有的类型都能够相互断言,只有A
包含B
的所有属性,或者B
包含A
的所有属性,A
和B
才能相互断言,举个栗子:
interface Animal { name: string; } interface Cat { name: string; run(): void; } let tom: Cat = { name: "tom", run() { console.log("i can run"); }, }; let anmimal: Animal = tom; tom.run(); (anmimal as Cat).run(); 复制代码
如果我们加一个新的interface:
let coffeeCup: Cup = { width: 20, height: 60, }; let anmimalCup: Animal = coffeeCup; 复制代码
就会抛出错误类型 "Cup" 中缺少属性 "name",但类型 "Animal" 中需要该属性。
总结
类型断言的用途:
- 联合类型可以断言为其中一个类型
- 父类可以被断言为自类
- 任何类型可以断言成 any
- any可以断言成任何类型
A
包含B
的所有属性,或者B
包含A
的所有属性,A
和B
才能相互断言
双重断言
双重断言意味着打破 「A
包含B
的所有属性,或者B
包含A
的所有属性,A
和B
才能相互断言」的规则,举个栗子:
interface Cat { run(): void; } interface Fish { siwm(): void; } function testCat(cat: Cat) { return cat as any as Fish; } let tom: Cat = { run() { console.log("i can run"); }, }; testCat(tom); 复制代码
声明
declare var
声明全局变量
declare var username: string; 复制代码
declare function
定义全局函数
declare function getToken(key: string): string