【TypeScript】超详细的笔记式教程【中】
数组
基本定义
在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 = {