TypeScript初体验(三)

1、交叉类型(&

交叉类型(&):功能类似于接口继承(extends),用于组合多个类型为一个类型(常用于对象类型)

代码示例:

interface Person {
    name: string
    say():number
}
interface Contact{
    phone: string
}
type PersonDetail = Person & Contact
let obj: PersonDetail = {
    name: '张三',
    phone: '13812345678',
    say() {
        return 1
    },
}

 交叉类型和接口继承的对比:

相同点:都可以实现对象类型的组合

不同点:两种方式实现类型组合时,对于同名属性之间,处理类型冲突的方式不同。接口继承会报错(类型不兼容);交叉类型不会报错,会兼容

代码示例:

//接口继承会报错
// interface A{
//     fn:(value: number)=>string
// }
// interface B extends A{
//     fn:(value: string)=>string
// }

//交叉类型不会报错
interface A{
    fn:(value: number)=>string
}
interface B{
    fn:(value: string)=>string
}
type C = A & B
let p: C = {
    fn(value: number | string) {
        return ''
    }
}

 2、泛型

泛型是可以在保证类型安全的前提下,让函数与多种类型一起工作,从而实现复用,常用于:函数、接口、class中。当想要接受多种不同的类型的时候可以使用泛型。

 代码示例:

function id<Type>(value: Type): Type {
    return value
}
id<number>(1)
id('a')

解释:

1、语法:在函数名称的后面添加<>(尖括号),尖括号中指定具体的类型

2、当传入类型number后,这个类型就会被函数声明时指定的类型变量Type捕获到。

3、此时,Type的类型就是number,所以,函数id参数和返回值的类型也都是number。

4、Type是自己定义的名称,也可以叫其他名称。

5、在调用泛型函数时,可以省略<类型>来简化泛型函数的调用,TS内部会采用类型参数推断的机制来推断出Type的类型。

6、当编译器无法推断类型或推断的类型不准确时,就需要显示的传入类型参数

 泛型约束

1、指定更加具体的类型

2、添加约束

 代码示例:

//指定更加具体的类型
function id<Type>(value: Type[]): Type[] {
    console.log(value.length);
    return value
}
id<number>([1])

//添加约束
interface ILength {
    length: number
}
function id2<Type extends ILength>(value: Type): Type{
    value.length
    return value
}
id2({ length: 7, name: 's' })
id2([1,22,'w'])

 添加约束

通过extends关键字使用接口,为泛型添加约束

该约束表示:传入的类型必须具有length属性,传入的实参只要有length属性即可

泛型的类型变量可以有多个,并且类型变量之间还可以约束

代码示例:

function getProp<Type, Key extends keyof Type>(obj: Type, key: Key) {
    return obj[key]
}
getProp([1, 2, 3], 1)
getProp('abc', 'split')
getProp('abc',10)//此处的10表示索引
console.log(getProp({name:'a',age:19},'age'));

解释:

1、添加了第二个类型变量key,两个类型变量之间使用(,逗号分隔。

2、keyof关键字接受一个对象类型,生成其键名称(可能是字符串或数字)的联合类型。

3、上面示例中第四个函数调用中的keyof Type实际上获取的是对象所有键的联合类型,也就是:'name'|'age'

4、类型变量Key受Type约束,Key只能是Type所有键中的任意一个,或者说只能访问对象中存在的属性。

5、Key名称可以自己定义成任何符合的变量名称

泛型接口

接口也是可以配合泛型来使用,以增强其灵活性,增强复用性 

 代码示例:

interface IdFunc<Type>{
    id: (value: Type) => Type
    ids: () => Type[]
}
let obj2: IdFunc<number> = {
    id(value) {
        return value
    },
    ids() {
        return [1,2]
    },
}

解释:

1、在接口名称的后面添加<类型变量>,那么,这个接口就变成了泛型接口。

2、接口中的类型变量,对接口中所有其他成员可见,也就是接口中所有成员都可以使用类型变量

3、使用泛型接口时,需要显示指定具体的类型

 实际上,JS中的数组在TS中就是一个泛型接口

当我们在使用数组时(如map、forEach等),TS会根据数组的不同类型,来自动将类型变量设置为相应的类型

可以通过Ctrl+鼠标左键来查看具体的类型信息

 泛型类

class也可以配合泛型来使用

代码示例:

class GenericNumber<NumType>{
    defaultValue: NumType
    add: (x: NumType, y: NumType) => NumType
    constructor(value: NumType) {
        this.defaultValue = value
    }
}
const myNum = new GenericNumber<number>(122)//可以省略<类型>不写
myNum.defaultValue = 10

解释:

1、类似于泛型接口,在class名称后面添加<类型变量>,这个类就变成了泛型类

2、类似于泛型接口,在创建class实例时,在类名后面通过<类型>来指定明确的类型

3、此处的add方法,采用的是箭头函数形式的类型书写方式

泛型工具类型 

 TS内置了一些常用的工具类型,他们都是基于泛型实现的

主要有:

1、Partial<Type>:将Type的所有属性设置为可选,不会改变传入类型的本身,而是创建一个新的类型。

2、Readonly<Type>:将Type的所有属性都设置为只读

3、Pick<Type, Keys>:从Type中选择一组属性来构造新类型,第二个变量传入的属性必须是第一个类型变量中存在的属性。

4、Record<Keys,Type>:构造一个对象类型,属性键为Keys表示对象有哪些属性,属性类型为Type表示对象属性的类型,

 代码示例:

interface Props {
    id: string
    children: number[]
    title: string
}
type PartialProps = Partial<Props>
type pickProps = Pick<Props, 'id' | 'title'>
type RecordObj = Record<'a' | 'b' | 'c', string[]>
let c: RecordObj = {
    a: ['a'],
    b: ['1'],
    c: ['c','3']
}

3、索引签名类型

使用场景:无法确定对象中有哪些属性(或者说对象中可以出现任意多个属性)

代码示例:

interface AnyObj {
    [key: string]: number
}
let obj: AnyObj = {
    age:19
}

解释:

1、使用[key: string]来约束该接口中允许出现的属性名称。表示只要是string类型的属性名称,都可以出现在对象中。

2、key只是一个占位符,可以换成任意合法的变量名称

3、JS中对象{}里的键是string类型的

 在数组对应的泛型接口中,也可以用到索引签名类型

代码示例:

interface MyArray<T> {
    [index: number]: T
}
let n: MyArray<number> = [1,2]

解释:

只要是number类型的键(索引)都可以出现在数组中,同时也要符合数组索引是number类型这一前提

 4、映射类型

映射类型:基于旧类型创建新类型(对象类型),减少重复、提升开发效率

 代码示例:

type PropKeys = 'x' | 'y' | 'z'
type Type2 = { x: number; y: number; z: number }
type Type1 = { [key in PropKeys]: number }
let s: Type1 = {
    x: 1,
    y: 2,
    z: 3
}

解释:

1、映射类型是基于索引签名类型的,所以,该语法也使用了[]

2、key in PropKeys表示key可以是PropKeys联合类型中的任意一个,类似于forin(let k in obj)

3、Type2和Type1结构完全相同

4、注意:映射类型只能在类型别名中使用,不能在接口中使用

 映射类型还可以根据对象类型来创建

代码示例:

type Props = { a: number; b: string; c: boolean }
type Type3 = { [key in keyof Props]: number }
let t: Type3 = {
    a: 1,
    b: 2,
    c: 3
}

解释:

1、先执行keyof Props获取到对象类型Props中所有键的联合类型:‘a'|'b'|'c'

2、然后,key in ...就表示key可以是Props中所有键名称中的任意一个

索引查询类型T[P] 语法

用来查询属性的类型,[]中的属性必须存在与被查询类型中,否则会报错

 代码示例:

type Props = { a: number; b: string; c: boolean }
type TypeA = Props['a']

TypeA的类型变成了number

 索引查询类型也可以同时查询多个索引类型

代码示例:

type Props = { a: number; b: string; c: boolean }
type TypeB = Props['a' | 'b']
type TypeC = Props[keyof Props]

解释:

1、可以使用字符串字面量的联合类型 |

2、也可以使用keyof操作符获取Props中的所有键对应的类型

5、类型声明文件

类型声明文件:用来为已存在的JS库提供类型信息

TS中有两种文件类型:1、.ts文件  2、.d.ts文件

 .ts文件

1、既包含类型信息又可以执行代码

2、可以被编译为JS文件,然后,执行代码

3、用途:编写程序代码的地方

 .d.ts文件

1、只包含类型信息的类型声明文件

2、不会生成JS文件,仅用于提供类型信息

3、用途:为JS提供类型信息

 使用已有的类型声明文件:

1、内置类型声明文件 :TS为JS运行时可用的所有标准化内置API都提供了声明文件

2、第三方库的类型声明文件:库自带类型声明文件或由DefinitelyTyped提供

DefinitelyTyped是一个GitHub仓库,用来提高质量TypeScript类型声明,可以通过npm/yarn来下载TS类型声明包,这些包的名称格式为:@type/*,例如:@type/react等。

TS官方文档:TypeScript: JavaScript With Syntax For Types.

TS中文官方:TypeScript: 中文网 官网 官方.

 6、创建自己的类型声明文件

项目内共享类型

使用场景:如果多个.ts文件中都使用到了同一个类型,可以创建.d.ts文件提供该类型,实现类型共享

 操作步骤:

1、创建index.d.ts类型声明文件

2、创建需要共享的类型,并使用export导出

3、在需要使用共享类型的.ts文件中,通过import导入即可,.d.ts后缀不用加

import {Props} from './index'

 为已有JS文件提供类型声明

使用场景:

1、在将JS项目迁移到TS项目时,为了让已有的.js文件有类型声明

2、成为库作者,创建库给其他人使用

declare关键字:用于类型声明,为其他地方(比如.js文件)已存在的变量声明类型,而不是创建一个新的变量。 

代码示例:

declare let count: number

 注意:

1、对于type、interface等这些明确就是TS类型的(只在TS中使用的),可以省略declare关键字

2、对于let、function等具有双重含义(在JS、TS中都能使用的),应该使用declare关键字

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值