在React中使用
使用 TypeScript
编写 React
代码,除了需要 TypeScript
这个库之外,还需要安装 @types/react
、@types/react-dom
npm i @types/react -s
npm i @types/react-dom -s
目前非常多的 JavaScript
库并没有提供自己关于 TypeScript
的声明文件
所以,ts
并不知道这些库的类型以及对应导出的内容,这里 @types
实际就是社区中的 DefinitelyTyped
库,定义了目前市面上绝大多数的 JavaScript
库的声明。
定义数组
需要注意的是,与javascript不同,在typescript中,数组中的每一项都必须是所定义了的类型;如果需要像javascript中一样使用可以传入不同类型的数组,考虑使用tuple
定义数组常用的表示法有:
- 类型+方括号
const HappyNums:number[]=[1,2,3,4,5]
在这里我们定义了number的数组,所以传入的必须都是number类型
- 数组泛型
let HappyNums:Array<number>=[1,2,3,4,5]
3.接口表示数组、类数组
function getNums(){
let args:{
[index:number]=number;
length:number
}=arguments
}
我们使用接口定义了args,实际上也可以采用原生的定义,,如 IArguments
, NodeList
, HTMLCollection
等
tuple元组
数组合并了相同类型的对象,而元组(Tuple)合并了不同类型的对象
enum枚举
枚举(Enum)类型用于取值被限定在一定范围内的场景
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
定义函数
基本用法类似其余写法,注意:
- 会将添加了默认值的参数识别为可选参数
function buildName(firstName: string = 'Tom', lastName: string) {
return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let cat = buildName(undefined, 'Cat');
- 使用…rest参数获取函数中的剩余参数
定义对象——接口
interface,在ts中用来定义对象的类型,有点类似js中class和实例的关系,不同的是,接口定义的是需要被约束的对象的类型,只理解概念不如来看代码
interface Student{
readonly id:number
name:string;
age:number;
graduated:false;
mood?:string
}
let Lili:Student={
name:'lili',
age:15,
graduated:false,
}
我们定义了一个名为Student的接口,同时让变量Lili的类型是Student,这样,Lili的类型就必须与Student规定的一致。
- 可选属性:需要注意的是,我们在mood后添加了?,这说明这个属性是可选的,Lili可以有也可以没有。
- 只读属性:希望某一属性只读,在变量前加上readonly——
as const
使对象中每一个属性都为readonly
联合类型
联合类型(Union Types)表示取值可以为多种类型中的一种。
let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
let myFavoriteNumber: string | number;
myFavoriteNumber = true;
// index.ts(2,1): error TS2322: Type 'boolean' is not assignable to type 'string | number'.
// Type 'boolean' is not assignable to type 'number'.
联合类型使用 |
分隔每个类型。
这里的 let myFavoriteNumber: string | number
的含义是,允许 myFavoriteNumber
的类型是 string
或者 number
,但是不能是其他类型。
当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法。——as规避
联合类型的变量在被赋值的时候,会根据类型推论的规则推断出一个类型
类型断言
写法:
值 as 类型
用法:
- 联合类型中需要使用其中一个类型的方法时
当TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型中共有的属性或方法
interface Cat {
name: string;
run(): void;
}
interface Fish {
name: string;
swim(): void;
}
function isFish(animal: Cat | Fish) {
if (typeof animal.swim === 'function') {
return true;
}
return false;
}//这里会报错,因为swim是Fish的方法
我们可以使用类型断言小小地”欺骗“一下ts,这样就可以使用这个方法啦,具体的操作就是
interface Cat {
name: string;
run(): void;
}
interface Fish {
name: string;
swim(): void;
}
function isFish(animal: Cat | Fish) {
if (typeof (animal as Fish).swim === 'function') {
return true;
}
return false;
}//animal as Fish,ts会把animal认为是Fish,就可以使用其方法了
不过需要注意的是要确保你不会传入Cat类型的参数,不然代码在运行时依旧会报错
将父类断言为具体的子类——instanceof 与 as
当类之间有继承关系时,类型断言也是很常见的:
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
,这样的话这个函数就能接受 Error
或它的子类作为参数了。
但是由于父类 Error
中没有 code
属性,故直接获取 error.code
会报错,需要使用类型断言获取 (error as ApiError).code
。
大家可能会注意到,在这个例子中有一个更合适的方式来判断是不是 ApiError
,那就是使用 instanceof
:
class ApiError extends Error {
code: number = 0;
}
class HttpError extends Error {
statusCode: number = 200;
}
function isApiError(error: Error) {
if (error instanceof ApiError) {
return true;
}
return false;
}
上面的例子中,确实使用 instanceof
更加合适,因为 ApiError
是一个 JavaScript 的类,能够通过 instanceof
来判断 error
是否是它的实例。
但是有的情况下 ApiError
和 HttpError
不是一个真正的类,而只是一个 TypeScript 的接口(interface
),接口是一个类型,不是一个真正的值,它在编译结果中会被删除,当然就无法使用 instanceof
来做运行时判断了:
interface ApiError extends Error {
code: number;
}
interface HttpError extends Error {
statusCode: number;
}
function isApiError(error: Error) {
if (error instanceof ApiError) {
return true;
}
return false;
}
// index.ts:9:26 - error TS2693: 'ApiError' only refers to a type, but is being used as a value here.
此时就只能用类型断言,通过判断是否存在 code
属性,来判断传入的参数是不是 ApiError
了
- 将any断言为具体类型/断言具体类型为any
前者用于解决历史遗留代码或是别的库存在的问题,后者建议用在百分百确定代码不会出错的情况来规避掉ts麻烦的报错
- as const 将属性变为readonly
Typescript 支持在类型声明后加上 as const
,这样我们就相当于给每个属性添加 readonly
。
let t = {
myProperty: "hi"
myArr: [1, 2, 3]
} as const
现在,T 的所有属性值都是不可修改的。例如,t.myArr.push(1)
不能编译,给 myProperty
属性重新赋值同样也不能编译。
泛型
泛型指在定义的时候,不预先指定具体的类型,而是在使用的使用,才给变量指定类型,所以在定义的时候不会写出具体类型,而是用这样的形式表示:
我们这里定义了一个方法,为其传入length\value,表示创建一个为length长度的数组,并将该数组中的每一项都填充为value。我们希望数组中的每一项都是输入的value类型,所以我们可以给value设定一个泛型T,表示任意类型,后面的Array和value:T都是指的这个泛型,在传入的时候,只要确保他们的类型一致,就不会报错。
function createArray(length,value:T):Array<T>{
let res:T[]=[];
for(let i=0;i<length;i++){
res[i]=value
}
return res
}
泛型约束
在函数内部使用泛型变量的时候,由于事先不知道它是哪种类型,所以不能随意的操作它的属性或方法:
function loggingIdentity<T>(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
属性的变量。表现在代码上就是让泛型extends一个具有我们期待属性的接口:
interface Lengthwise{
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg:T):T{
conse.log(arg.length);
return arg;
}
泛型接口
泛型除了可以用在函数中,也可以用在接口中,下面我们来实现一个泛型接口:
interface getItem<T>{
(para:T):T
}
//使用时,在原本定义泛型的<T>传入参数的类型即可
const getBox:getItem<number>=para=>para
type
type person=string | number
使用场景:
- 字符串字面量: 取值只能是某几个字符串中的一个
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'
- 联合类型| 交叉类型& : 前者多选一,后者将类型合并为一个类型
- 类型别名:
类型别名会给一个类型起个新名字,类型别名有时和接口很像,但是可以作用于原始值、联合类型、元组以及其它任何你需要手写的类型
可以使用 type SomeName = someValidTypeAnnotation
的语法来创建类型别名
type person=string | number
const NamePerson:person=string
utility type
Partial
:将传入的类型设置为可选
function updateBook<T extends Book>(book: T, updates: Partial<T>) {
const updatedBook = {...book, ...updates }
notifyServer(updatedBook)
return updatedBook
}
//原理:
type Partial<T> = {
[P in keyof T]?: T[P];
}
**Pick
**挑选一组属性并组成一个新的类型。
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
Omit
:删除可选类型
type Person={
name:string.
age:number,
isStudent:true,}
//使得lili可以没有age和isStudent属性,也可以不传入任何属性
const lili:Partial<Person>={name:'lili'}
//第一个参数:类型;第二个参数:要删除的类型
const jack:Omit<Person,'age'|'isStudent'>={name:'jack'}
Required
从接口声明中移除具有 ?
属性的值。
interface T {
maybeName?: string
}
type CertainT = Required<T> // 等价于 { maybeName: string }