资讯专栏INFORMATION COLUMN

为vue3学点typescript, 解读高级类型

chuyao / 2254人阅读

摘要:直达第一课体验第二课基础类型和入门高级类型第三课泛型第四课解读高级类型第五课什么是命名空间回顾第二课的时候为了更好的讲解基础类型所以我们讲解了一部分高级类型比如接口联合类型交叉类型本节课我会把剩余高级类型都讲完知识点摘要本节课主要关键词为自

直达

第一课, 体验typescript

第二课, 基础类型和入门高级类型

第三课, 泛型

第四课, 解读高级类型

第五课, 什么是命名空间(namespace)?

回顾

第二课的时候为了更好的讲解基础类型, 所以我们讲解了一部分高级类型, 比如"接口( interface )" / "联合类型( | )" / "交叉类型( & )", 本节课我会把剩余高级类型都讲完.

知识点摘要

本节课主要关键词为: 自动类型推断 / 类型断言 / 类型别名(type) / 映射类型(Pick/Record等...) / 条件类型(extends) / 类型推断(infer)

自动类型推断(不用你标类型了,ts自己猜)

第二课我们讲了那么多基础类型, 大家现在写ts的时候一定会在每个变量后面都加上类型吧? 但是?

现在告诉大家有些情况下你不需要标注类型, ts可以根据你写的代码来自动推断出类型:

赋值字面量给变量
let n = 1; // ts会自动推断出n是number类型
n+=3 // 不报错,因为已知类型

let arr1 = []; // 类型为: any[]
arr1.push(1,"2", {o:3});

let arr = [1]; // 内部要有数字, 才能推断出正确类型
arr.push(2);
自动阅读if条件判断
let n: number|null = 0.5 < Math.random() ? 1:null;
if(null !== n){
    n+=3 // ts知道现在n不是null是number
}
浏览器自带api
document.ontouchstart = ev=>{ // 能自动推断出ev为TouchEvent
    console.log(ev.touches);  // 不报错, TouchEvent上有touches属性
}
typeof

typeof就是js中的typeof, ts会根据你代码中出现的typeof来自动推断类型:

let n:number|string = 0.5 < Math.random()? 1:"1";

// 如果没有typeof, n*=2会报错, 提示没法推断出当前是number类型, 不能进行乘法运算
if("number" === typeof n) {
    n*= 2;
} else  {
    n= "2";
}

注意: 在ts文档中, 该部分的知识点叫做typeof类型保护, 和其他类型推断的内容是分开的, 被写在高级类型/类型保护章节中.

instanceof

ts会根据你代码中出现的instanceof来自动推断类型:

let obj = 0.5 < Math.random() ? new String(1) : new Array(1);
if(obj instanceof String){
    // obj推断为String类型
    obj+= "123"
} else {
    // obj为any[]类型
    obj.push(123);
}

注意: 在ts文档中, 该部分的知识点叫做instanceof类型保护, 和其他类型推断的内容是分开的, 被写在高级类型/类型保护章节中.

类型断言(你告诉ts是什么类型, 他都信)

有些情况下系统没办法自动推断出正确的类型, 就需要我们标记下, 断言有2种语法, 一种是通过"<>", 一种通过"as", 举例说明:

let obj = 0.5 < Math.random() ? 1 : [1]; // number|number[]

// 断言, 告诉ts, obj为数组
(obj).push(1);

//等价
(obj as number[]).push(1);
类型别名(type)

类型别名可以表示很多接口表示不了的类型, 比如字面量类型(常用来校验取值范围):

type A = "top"|"right"|"bottom"|"left"; // 表示值可能是其中的任意一个
type B = 1|2|3;
type C = "红"|"绿"|"黄";
type D = 150;

let a:A = "none"; // 错误, A类型中没有"none"
更多组合:
interface A1{
    a:number;
}
type B = A1 | {b:string};
type C = A1 & {b:string};

// 与泛型组合
type D = A1 | T[];
索引类型(keyof)

js中的Object.keys大家肯定都用过, 获取对象的键值, ts中的keyof和他类似, 可以用来获取对象类型的键值:

type A = keyof {a:1,b:"123"} // "a"|"b"
type B = keyof [1,2] // "1"|"2"|"push"... , 获取到内容的同时, 还得到了Array原型上的方法和属性(实战中暂时没遇到这种需求, 了解即可)

可以获得键值, 也可以获取对象类型的值的类型:

type C = A["a"] // 等于type C = 1;
let c:C = 2 // 错误, 值只能是1
映射类型(Readonly, Pick, Record等...)

映射类型比较像修改类型的工具函数, 比如Readonly可以把每个属性都变成只读:

type A  = {a:number, b:string}
type A1 = Readonly // {readonly a: number;readonly b: string;}

打开node_modules/typescript/lib文件夹可以找到lib.es5.d.ts, 在这我们能找到Readonly的定义:

type Readonly = {
    readonly [P in keyof T]: T[P];
};

其实不是很复杂, 看了本节课前面前面的内容, 这个很好理解是吧:

定义一个支持泛型的类型别名, 传入类型参数T.

通过keyof获取T上的键值集合.

in表示循环keyof获取的键值.

添加readonly标记.

Partial, 让属性都变成可选的
type A  = {a:number, b:string}
type A1 = Partial // { a?: number; b?: string;}
Required, 让属性都变成必选
type A  = {a?:number, b?:string}
type A1 = Required // { a: number; b: string;}
Pick, 只保留自己选择的属性, U代表属性集合
type A  = {a:number, b:string}
type A1 = Pick //  {a:number}
Omit 实现排除已选的属性
type A  = {a:number, b:string}
type A1 = Omit // {b:string}
Record, 创建一个类型,T代表键值的类型, U代表值的类型
type A1 = Record // 等价{[k:string]:string}
Exclude, 过滤T中和U相同(或兼容)的类型
type A  = {a:number, b:string}
type A1 = Exclude // number

// 兼容
type A2 = Exclude // never , 因为any兼容number, 所以number被过滤掉
Extract, 提取T中和U相同(或兼容)的类型
type A  = {a:number, b:string}
type A1 = Extract // string
NonNullable, 剔除T中的undefined和null
type A1 = NonNullable // number|string
ReturnType, 获取T的返回值的类型
type A1= ReturnType<()=>number> // number
InstanceType, 返回T的实例类型

ts中类有2种类型, 静态部分的类型和实例的类型, 所以T如果是构造函数类型, 那么InstanceType可以返回他的实例类型:

interface A{
    a:HTMLElement;
}

interface AConstructor{
    new():A;
}

function create (AClass:AConstructor):InstanceType{
    return new AClass();
}
Parameters 获取函数参数类型

返回类型为元祖, 元素顺序同参数顺序.

interface A{
    (a:number, b:string):string[];
}

type A1 = Parameters // [number, string]
ConstructorParameters 获取构造函数的参数类型

Parameters类似, 只是T这里是构造函数类型.

interface AConstructor{
    new(a:number):string[];
}

type A1 = ConstructorParameters // [number]
extends(条件类型)
T extends U ? X : Y

用来表示类型是不确定的, 如果U的类型可以表示T, 那么返回X, 否则Y. 举几个例子:

type A =  string extends "123" ? string :"123" // "123"
type B =  "123" extends string ? string :123 // string

明显string的范围更大, "123"可以被string表示, 反之不可.

infer(类型推断)

单词本身的意思是"推断", 实际表示在extends条件语句中声明待推断的类型变量. 我们上面介绍的映射类型中就有很多都是ts在lib.d.ts中实现的, 比如Parameters:

type Parameters any> = T extends (...args: infer P) => any ? P : never;
为vue3学点typescript, 解读高级类型 直达

第一课, 体验typescript

第二课, 基础类型和入门高级类型

第三课, 泛型

第四课, 解读高级类型

回顾

第二课的时候为了更好的讲解基础类型, 所以我们讲解了一部分高级类型, 比如"接口( interface )" / "联合类型( | )" / "交叉类型( & )", 本节课我会把剩余高级类型都讲完.

知识点摘要

本节课主要关键词为: 自动类型推断 / 类型断言 / 类型别名(type) / 映射类型(Pick/Record等...) / 条件类型(extends) / 类型推断(infer)

自动类型推断(不用你标类型了,ts自己猜)

第二课我们讲了那么多基础类型, 大家现在写ts的时候一定会在每个变量后面都加上类型吧? 但是?

现在告诉大家有些情况下你不需要标注类型, ts可以根据你写的代码来自动推断出类型:

赋值字面量给变量
let n = 1; // ts会自动推断出n是number类型
n+=3 // 不报错,因为已知类型

let arr1 = []; // 类型为: never[]
arr1.push(1); // 报错,

let arr = [1]; // 内部要有数字, 才能推断出正确类型
arr.push(2);
自动阅读if条件判断
let n: number|null = 0.5 < Math.random() ? 1:null;
if(null !== n){
    n+=3 // ts知道现在n不是null是number
}
浏览器自带api
document.ontouchstart = ev=>{ // 能自动推断出ev为TouchEvent
    console.log(ev.touches);  // 不报错, TouchEvent上有touches属性
}
typeof

typeof就是js中的typeof, ts会根据你代码中出现的typeof来自动推断类型:

let n:number|string = 0.5 < Math.random()? 1:"1";

// 如果没有typeof, n*=2会报错, 提示没法推断出当前是number类型, 不能进行乘法运算
if("number" === typeof n) {
    n*= 2;
} else  {
    n= "2";
}

注意: 在ts文档中, 该部分的知识点叫做typeof类型保护, 和其他类型推断的内容是分开的, 被写在高级类型/类型保护章节中.

instanceof

ts会根据你代码中出现的instanceof来自动推断类型:

let obj = 0.5 < Math.random() ? new String(1) : new Array(1);
if(obj instanceof String){
    // obj推断为String类型
    obj+= "123"
} else {
    // obj为any[]类型
    obj.push(123);
}

注意: 在ts文档中, 该部分的知识点叫做instanceof类型保护, 和其他类型推断的内容是分开的, 被写在高级类型/类型保护章节中.

类型断言(你告诉ts是什么类型, 他都信)

有些情况下系统没办法自动推断出正确的类型, 就需要我们标记下, 断言有2种语法, 一种是通过"<>", 一种通过"as", 举例说明:

let obj = 0.5 < Math.random() ? 1 : [1]; // number|number[]

// 断言, 告诉ts, obj为数组
(obj).push(1);

//等价
(obj as number[]).push(1);
类型别名(type)

类型别名可以表示很多接口表示不了的类型, 比如字面量类型(常用来校验取值范围):

type A = "top"|"right"|"bottom"|"left"; // 表示值可能是其中的任意一个
type B = 1|2|3;
type C = "红"|"绿"|"黄";
type D = 150;

let a:A = "none"; // 错误, A类型中没有"none"
更多组合:
interface A1{
    a:number;
}
type B = A1 | {b:string};
type C = A1 & {b:string};

// 与泛型组合
type D = A1 | T[];
索引类型(keyof)

js中的Object.keys大家肯定都用过, 获取对象的键值, ts中的keyof和他类似, 可以用来获取对象类型的键值:

type A = keyof {a:1,b:"123"} // "a"|"b"
type B = keyof [1,2] // "1"|"2"|"push"... , 获取到内容的同时, 还得到了Array原型上的方法和属性(实战中暂时没遇到这种需求, 了解即可)

可以获得键值, 也可以获取对象类型的值的类型:

type C = A["a"] // 等于type C = 1;
let c:C = 2 // 错误, 值只能是1
映射类型(Readonly, Pick, Record等...)

映射类型比较像修改类型的工具函数, 比如Readonly可以把每个属性都变成只读:

type A  = {a:number, b:string}
type A1 = Readonly // {readonly a: number;readonly b: string;}

打开node_modules/typescript/lib文件夹可以找到lib.es5.d.ts, 在这我们能找到Readonly的定义:

type Readonly = {
    readonly [P in keyof T]: T[P];
};

其实不是很复杂, 看了本节课前面前面的内容, 这个很好理解是吧:

定义一个支持泛型的类型别名, 传入类型参数T.

通过keyof获取T上的键值集合.

in表示循环keyof获取的键值.

添加readonly标记.

Partial, 让属性都变成可选的
type A  = {a:number, b:string}
type A1 = Partial // { a?: number; b?: string;}
Required, 让属性都变成必选
type A  = {a?:number, b?:string}
type A1 = Required // { a: number; b: string;}
Pick, 只保留自己选择的属性, U代表属性集合
type A  = {a:number, b:string}
type A1 = Pick //  {a:number}
Omit 实现排除已选的属性
type A  = {a:number, b:string}
type A1 = Omit // {b:string}
Record, 创建一个类型,T代表键值的类型, U代表值的类型
type A1 = Record // 等价{[k:string]:string}
Exclude, 过滤T中和U相同(或兼容)的类型
type A  = {a:number, b:string}
type A1 = Exclude // number

// 兼容
type A2 = Exclude // never , 因为any兼容number, 所以number被过滤掉
Extract, 提取T中和U相同(或兼容)的类型
type A  = {a:number, b:string}
type A1 = Extract // string
NonNullable, 剔除T中的undefined和null
type A1 = NonNullable // number|string
ReturnType, 获取T的返回值的类型
type A1= ReturnType<()=>number> // number
InstanceType, 返回T的实例类型

ts中类有2种类型, 静态部分的类型和实例的类型, 所以T如果是构造函数类型, 那么InstanceType可以返回他的实例类型:

interface A{
    a:HTMLElement;
}

interface AConstructor{
    new():A;
}

function create (AClass:AConstructor):InstanceType{
    return new AClass();
}
Parameters 获取函数参数类型

返回类型为元祖, 元素顺序同参数顺序.

interface A{
    (a:number, b:string):string[];
}

type A1 = Parameters // [number, string]
ConstructorParameters 获取构造函数的参数类型

Parameters类似, 只是T这里是构造函数类型.

interface AConstructor{
    new(a:number):string[];
}

type A1 = ConstructorParameters // [number]
extends(条件类型)
T extends U ? X : Y

用来表示类型是不确定的, 如果U的类型可以表示T, 那么返回X, 否则Y. 举几个例子:

type A =  string extends "123" ? string :"123" // "123"
type B =  "123" extends string ? string :123 // string

明显string的范围更大, "123"可以被string表示, 反之不可.

infer(类型推断)

单词本身的意思是"推断", 实际表示在extends条件语句中声明待推断的类型变量. 我们上面介绍的映射类型中就有很多都是ts在lib.d.ts中实现的, 比如Parameters:

type Parameters any> = T extends (...args: infer P) => any ? P : never;

上面声明一个P用来表示...args可能的类型, 如果(...args: infer P)可以表示 T, 那么返回...args对应的类型, 也就是函数的参数类型, 反之返回never.

注意: 开始的T extends (...args: any) => any用来校验输入的T是否是函数, 如果不是ts会报错, 如果直接替换成T不会有报错, 会一直返回never.

应用infer

接下来我们利用infer来实现"删除元祖类型中第一个元素", 这常用于简化函数参数, 这有一个我之前的应用

export type Tail = ((...args: Tuple) => void) extends ((a: any, ...args: infer T) => void) ? T : never;
总结

多写多练, 很快就上手, 放几个我用ts写的项目当做参考, 抛砖引玉, 加油!

手势库: https://github.com/any86/any-...

命令式调用vue组件: https://github.com/any86/vue-...

工作中常用的一些代码片段: https://github.com/any86/usef...

一个mini的事件管理器: https://github.com/any86/any-...
上面声明一个P用来表示...args可能的类型, 如果(...args: infer P)可以表示 T, 那么返回...args对应的类型, 也就是函数的参数类型, 反之返回never.

注意: 开始的T extends (...args: any) => any用来校验输入的T是否是函数, 如果不是ts会报错, 如果直接替换成T不会有报错, 会一直返回never.

应用infer

接下来我们利用infer来实现"删除元祖类型中第一个元素", 这常用于简化函数参数, 这有一个我之前的应用

export type Tail = ((...args: Tuple) => void) extends ((a: any, ...args: infer T) => void) ? T : never;
总结

多写多练, 很快就上手, 放几个我用ts写的项目当做参考, 抛砖引玉, 加油!

手势库: https://github.com/any86/any-...

命令式调用vue组件: https://github.com/any86/vue-...

工作中常用的一些代码片段: https://github.com/any86/usef...

一个mini的事件管理器: https://github.com/any86/any-...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/106216.html

相关文章

  • vue3学点typescript(1), 体验typescript

    摘要:更新啦第一课体验第二课基础类型和入门高级类型第三课泛型第四课解读高级类型要来了看了的视频特别兴奋要来了是用开发的我揣测在的带领下会成为主流呢要不先学点年最酷的前端技术我是年初开始使用的自从开始用上了就喜欢上了真的爱不释手最爱他几点很多小错误 更新啦 第一课, 体验typescript 第二课, 基础类型和入门高级类型 第三课, 泛型 第四课, 解读高级类型 vue3要来了 看了vue ...

    Batkid 评论0 收藏0
  • vue3学点typescript, 基础类型和入门高级类型

    摘要:导航第一课体验第二课基础类型和入门高级类型第三课泛型第四课解读高级类型很重要这一节很重要可以说是的最核心部分这一节学完其实就可以开始用写代码了想想中的再看看标题中的类型字所以请大家务必认真什么是入门高级类型因为高级类型的内容比较多但是有些基 导航 第一课, 体验typescript 第二课, 基础类型和入门高级类型 第三课, 泛型 第四课, 解读高级类型 很重要 这一节很重要, 可以说...

    Songlcy 评论0 收藏0
  • vue3学点typescript, 泛型

    摘要:往期第一课体验第二课基础类型和入门高级类型第三课泛型第四课解读高级类型插一课本来打算接着上节课把高级类型都讲完但是写着写着我发现高级类型中有很多地方都需要泛型的知识那么先插一节泛型什么是类型变量和泛型变量的概念我们都知道可以表示任意数据类型 往期 第一课, 体验typescript 第二课, 基础类型和入门高级类型 第三课, 泛型 第四课, 解读高级类型 插一课 本来打算接着上节课, ...

    tianlai 评论0 收藏0
  • Vue3 学点 TypeScript, 什么是命名空间(namespace)

    摘要:往期目录第一课体验第二课基础类型和入门高级类型第三课什么是泛型第四课解读高级类型第五课什么是命名空间什么时候要用命名空间如果你发现自己写的功能函数类接口等越来越多你想对他们进行分组管理就可以用命名空间下面先用类举例仔细看你会发现下还有在这里 往期目录 第一课, 体验typescript 第二课, 基础类型和入门高级类型 第三课, 什么是泛型? 第四课, 解读高级类型 第五课, 什么是命...

    greatwhole 评论0 收藏0

发表评论

0条评论

chuyao

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<