细说Typescript类型检查机制
上一篇文章我介绍了Typescript的基础知识,回顾一下,有基础数据类型、数组、函数、类、接口、泛型等,本节内容将述说一下Typescript为方便我们开发提供了一些类型检查机制。
类型检查机制
类型检查机制: Typescript编译器在做类型检查时,所秉承的一些原则,以及表现出的一些行为。
作用:辅助开发,提高开发效率。主要有以下三点:
- 类型推断
- 类型兼容性
- 类型保护
1、类型推断
不需要指定变量的类型(函数的返回值类型), Typescript可以根据某些规则自动地为其推断出一个类型。
- 基础类型推断。是ts中比较常见的类型推断。
- // 没有设置类型时,TS会根据赋值的类型推断类型
let a1; // 等价于 let a1: any- let a = 1; // 等价于 let a: number
- let b = [1]; // 等价于 let b: number[]
- let c = [1, null]; // 等价于 let c: number[]
- let c1 = (x = 1) => {} // 等价于 let c1: (x?: number) => void
- let d = (x = 1) => x + 1; // 等价于 let d: (x?: number) => number
- 最佳通用类型推断
- // 断言好处:改造旧代码会很有效,但避免滥用,需对上下文环境有充足的预判,否则会带来安全隐患
- interface Foo {
- bar: number
- }
- // let foo = {}; // 报错
- // let foo = {} as Foo; // 注意:不可滥用断言,若去掉bar属性,foo对象就没有按照接口的严格约定返回,就会遗漏
- // foo.bar = 1;
- let foo: Foo = { // 最佳通用类型推断
- bar: 1
- };
- 总结:TS的类型推断可以为我们提供一些重要的辅助信息,但是需要合理使用
2、类型兼容性
当一个类型 y 可以被赋值给另一个类型 x 时,我们就可以说类型 x 兼容类型 y x 兼容 y : x (目标类型)= y(源类型);
简单理解:
接口之间兼容:成员少的兼容成员多的
函数之间兼容:参数多的兼容参数少的
2.1 类型兼容
- // x 兼容 y : x (目标类型) = y(源类型)
- let s: string = 'a'
- s = null; // 报错
上述的报错,在tsconfig.json 设置 strictNullChecks:false即可
2.2 接口兼容
接口之间兼容:成员少的会兼容成员多的
- interface XX {
- a: any;
- b: any;
- }
- interface YY {
- a: any;
- b: any;
- c: any;
- }
- let xx: XX = {a:1, b:2}
- let yy: YY = {a:1, b:2, c:3}
- xx = yy;
- // yy = xx; // 报错。注意:源类型必须要具备目标类型的必要属性。xx可以兼容yy类型,反之不行
2.3 函数兼容
判断两个函数是否兼容,一般会发生在相互赋值的场景下。
函数之间兼容:参数多的兼容参数少的
- type Handler = (a: number, b: number) => void; // Handler 为目标类型,传入的参数为源类型
- function hocf(handler: Handler){
- return handler
- }
- // 目标函数要兼容源函数需要同时满足以下三点:
- // 1) 参数个数要求: 目标函数的个数要多于源函数参数的个数
- // 固定参数个数
- let handler1 = (a: number) => {};
- hocf(handler1);
- // let handler2 = (a: number, b: number, c: number) => {};
- // hocf(handler2); // 报错。目标函数的个数要多于源函数参数的个数
- // 不固定参数:可选参数和剩余参数: 需坚持三原则
- let l = (p1: number, p2: number) => {}
- let m = (p1?: number, p2?: number) => {}
- let n = (...args: number[]) => {}
- // - 1.固定参数可以兼容可选参数和剩余参数
- l = m;
- l = n;
- // - 2.可选参数不兼容固定参数和剩余参数,但设置 tsconfig strictFunctionTypes:false 属性,可预防报错
- m = n // 报错
- m = n // 报错
- // - 3.剩余参数可以兼容固定参数和可选参数
- n = l
- n = m
- // 2) 参数类型要求
- // 基础类型接口
- let handler3 = (a: string) => {};
- // hocf(handler3); // 报错。类型不兼容
- // 对象类型接口:成员多的兼容成员少的,与接口成员兼容相反,可以看成参数,参数多的兼容参数少的
- interface Point3D {
- x: number;
- y: number;
- z: number;
- }
- interface Point2D {
- x: number;
- y: number;
- }
- // 参数个数相同,参数类型相同Object
- let p3d = (point: Point3D) => {}
- let p2d = (point: Point2D) => {}
- p3d = p2d
- p2d = p3d // 报错。设置 tsconfig strictFunctionTypes:false 属性,可预防报错
- // 函数参数之间可以相互赋值的情况被称为 参数双向协变。作用:可以把精确类型 赋值给 不精确类型,这样就不需要把不精确的类型断言成精确类型
- // 3) 返回值类型。ts要求我们目标函数的返回值类型必须要和源函数返回值类型一致或者是其子类型
- // 成员少的兼容成员多的
- let o = () => ({name: "Alice"});
- let p = () => ({name: "Alice", location: 'Beijing'});
- o = p;
- // p = o; // 报错
2.4 函数重载兼容
- // 在重载中,目标函数的参数要多于源函数的参数
- function overload(a: number, b: number): number // 列表中的函数:目标函数
- function overload(a: string, b: string): string // 列表中的函数:目标函数
- function overload(a: any, b: any): any {} // 具体实现:源函数
- // function overload(a: any, b: any, c: any): any {} // 报错。在重载中,目标函数的参数要多于源函数的参数
- // function overload(a: any, b: any) {} // 报错。返回值参数类型不兼容
2.5 枚举兼容
- enum Fruit { Apple, Banana }
- enum Color { Red, Yellow }
- // 枚举类型和数字类型是完全可以互相兼容和赋值的
- let fruit: Fruit.Apple = 3;
- let no: number = Fruit.Apple;
- // 枚举类型之间是不兼容的
- // let color: Color.Red = Fruit.Apple; // 报错
2.6 类兼容
类兼容和接口兼容相似,都是只比较结构;
1. 构造函数constructor、和静态成员static是不作为比较的;
2. 如果两个类具有相同的实例成员,这两个类之间也是兼容的;
3. 类中有私有成员 private,两个类是不兼容的,只有父类和子类是兼容的;
- class A {
- constructor(p: number, q: number){}
- id: number = 1
- // private name: string = ''// 3. 报错
- }
- class B {
- static s = 1;
- constructor(p: number){}
- id: number = 2
- // private name: string = '' // 3. 报错
- }
- let aa = new A(1,2)
- let bb = new B(1)
- // 2. 都具有id属性,所以相互兼容
- aa = bb;
- bb = aa;
- // 3. 父类和子类是兼容的
- class An extends A { }
- let an = new An(1,2)
- aa = an;
- an = aa;
2.7 泛型兼容
- // - 不会报错
- interface Empty<T>{ }
- let obj1: Empty<number> = { }
- let obj2: Empty<number> = { }
- obj1 = obj2;
- // - 会报错 只有接口参数T被接口使用的时候,才会影响泛型的兼容性
- // interface Empty<T>{
- // value: T
- // }
- // let obj1: Empty<number> = {}
- // let obj2: Empty<number> = {}
- // obj1 = obj2;
- // 2. 泛型函数
- let loog1 = <T>(x: T): T => {
- console.log('x')
- return x
- }
- let loog2 = <U>(y: U): U => {
- console.log('y')
- return y
- }
- loog1 = loog2; // 两个泛型函数的定义相同,并没有指定类型参数,泛型函数也是兼容的
2.8 总结
1、考虑类型兼容是因为ts允许我们把一些类型不同的变量相互赋值,虽然在某种程度上讲,会产生不可靠的行为,但这个特性却增加了语言的灵活性
2、类型兼容的使用场景:广泛存在接口、函数、类中 演示
3、类型保护
定义: Typescript能够在特定的区块(类型保护区块)中保证变量属于某种确定的类型。 可以在此区块中放心地引用此类型的属性,或者调用此类型的方法。
换句话说,类型保护可以保证一个字符串是一个字符串,尽管它的值也可以是一个数值。
- enum Type { Strong, Week }
- class Java {
- helloJava(){
- console.log('Hello Java')
- }
- java: any
- }
- class JavaScript {
- helloJavaScript(){
- console.log('Hello JavaScript')
- }
- javascript: any
- }
- function getLanguage(type: Type, x: string|number){
- let lang = type === Type.Strong ? new Java() : new JavaScript();
- // 用类型断言解决返回值不确定的情况,可读性差
- if((lang as Java).helloJava){
- (lang as Java).helloJava()
- } else {
- (lang as JavaScript).helloJavaScript()
- }
- // console.log(lang, 'lang')
- return lang;
- }
上述的解决方案显然不是最佳方案,有以下四种方案可以解决类型保护:
3.1 方案1: instanceof
- function getLanguage(type: Type, x: string|number){
- let lang = type === Type.Strong ? new Java() : new JavaScript();
- if(lang instanceof Java){
- lang.helloJava()
- } else {
- lang.helloJavaScript()
- }
- // console.log(lang, 'lang')
- return lang;
- }
3.2 方案2: in
- function getLanguage(type: Type, x: string|number){
- let lang = type === Type.Strong ? new Java() : new JavaScript();
- if('java' in lang){
- lang.helloJava()
- } else {
- lang.helloJavaScript()
- }
- // console.log(lang, 'lang')
- return lang;
- }
3.3 方案3: typeof
- function getLanguage(type: Type, x: string|number){
- let lang = type === Type.Strong ? new Java() : new JavaScript();
- // x也是联合类型,typeof类型保护,可以判断出基本类型。
- if(typeof x === 'string'){
- x.length
- } else {
- x.toFixed
- }
- // console.log(lang, 'lang')
- return lang;
- }
3.4 方案4: 类型保护函数
- function getLanguage(type: Type, x: string|number){
- let lang = type === Type.Strong ? new Java() : new JavaScript();
- if(isJava(lang)){
- lang.helloJava()
- }else{
- lang.helloJavaScript()
- }
- // console.log(lang, 'lang')
- return lang;
- }
定义一个类型保护函数:
- // 类型保护函数,可以传递任何值给 isJava 函数,用来判断它是不是 Java。isJava 函数与普通函数的最大区别是,该函数的返回类型是 lang is Java,这就是 "类型谓词"。
- function isJava(lang: Java | JavaScript): lang is Java{
- // 类型谓词可参考:https://segmentfault.com/a/1190000022883470
- return (lang as Java).helloJava !== undefined // 返回结果是 true,那么当前判断的 lang 变量值的类型是 Java 类型。
- }
类型保护作用:
1. 可以提前对类型做预判
2. 返回类型谓词,如 lang is Java;
3. 包含可以准确确定给定变量类型的逻辑语句,如 (lang as Java).helloJava !== undefined。
3.5 总结
不同的判断方法有不同的使用场景:
typeof:判断一个变量的类型(多用于基本类型);
instanceof:判断一个实例是否属于某个类;
in:判断一个属性是否属于某个对象;
类型保护函数:某些判断可能不是一条语句能够搞定的,需要更多复杂的逻辑,适合封装到一个函数内。
结合ts类型检查机制再配合vscode 自动补全和自动修复功能,能够极大的提高我们的开发效率!
4、结尾
截止本节,Typescript的基础知识已经介绍差不多了,后期如有时间会继续更新TS的进阶高级部分,感谢您的阅读~
细说Typescript类型检查机制的更多相关文章
- TypeScript类型检查机制
类型推断 指不需要指定变量的类型,TS编译器可以根据某些规则自动推断出类型. 什么时候会有类型推断? 声明变量时没有指定类型 函数默认参数 函数返回值 ...... let a; // 这时自动推断为 ...
- 【长文详解】TypeScript、Babel、webpack以及IDE对TS的类型检查
只要接触过ts的前端同学都能回答出ts是js超集,它具备静态类型分析,能够根据类型在静态代码的解析过程中对ts代码进行类型检查,从而在保证类型的一致性.那,现在让你对你的webpack项目(其实任意类 ...
- TypeScript 类型推导及类型兼容性
类型推导就是在没有明确指出类型的地方,TypeScript编译器会自己去推测出当前变量的类型. 例如下面的例子: let a = 1; 我们并没有明确指明a的类型,所以编译器通过结果反向推断变量a的类 ...
- 类型检查和鸭子类型 Duck typing in computer programming is an application of the duck test 鸭子测试 鸭子类型 指示编译器将类的类型检查安排在运行时而不是编译时 type checking can be specified to occur at run time rather than compile time.
Go所提供的面向对象功能十分简洁,但却兼具了类型检查和鸭子类型两者的有点,这是何等优秀的设计啊! Duck typing in computer programming is an applicati ...
- CesiumJS新增官方TypeScript类型定义
Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com/ 在当前的1.70版本中,CesiumJS现在附带了正式的Type ...
- 解析 Linux 内核可装载模块的版本检查机制
转自:http://www.ibm.com/developerworks/cn/linux/l-cn-kernelmodules/ 为保持 Linux 内核的稳定与可持续发展,内核在发展过程中引进了可 ...
- 编译期类型检查 in ClojureScript
前言 话说"动态类型一时爽,代码重构火葬场",虽然有很多不同的意见(请参考),但我们看到势头强劲的TypeScript和Flow.js,也能感知到静态类型在某程度上能帮助我们写出 ...
- 享受Python和PHP动态类型检查语言的快感
前言 写这文章的时候特地查了资料,以确保我没有说错关于Python和PHP的类型机制. 所以这里放一张图,关于强弱类型与动态/静态类型检查的区分 从分类上看,PHP属于弱类型语言,而Python属于强 ...
- 介绍几款 Python 类型检查工具
近日,微软在 Github 上开源了一个 Python 静态类型检查工具:pyright ,引起了社区内的多方关注. 微软在开源项目上的参与力度是越来越大了,不说收购 Github 这种大的战略野心, ...
随机推荐
- 利用C++11可变模板,封装调用dll导出函数
起因 开发中经常需要动态调用一些导出函数,试着利用C++11特性封装一下 尝试 常规使用 typedef int WINAPI (*TMessageBoxA)(HWND hWnd,LPCSTR lpT ...
- Django orm 常用查询筛选总结
本文主要列举一下django orm中的常用查询的筛选方法: 大于.大于等于 小于.小于等于 in like is null / is not null 不等于/不包含于 其他模糊查询 model: ...
- Pytorch Torchvision Transform
Torchvision.Transforms Transforms包含常用图像转换操作.可以使用Compose将它们链接在一起. 此外,还有torchvision.transforms.functio ...
- 电脑桌面与群晖NAS双向实时同步-20210105
电脑桌面与群晖NAS双向实时同步 2021年1月15日星期五 一.购买群晖DS920+网络存储服务器.NEC超轻笔记本电脑(重量小于800克).小米10至尊版安卓智能手机和intel i9 1 ...
- 大都市meg DFS序
题目描述 在经济全球化浪潮的影响下,习惯于漫步在清晨的乡间小路的邮递员Blue Mary也开始骑着摩托车传递邮件了.不过,她经常回忆起以前在乡间漫步的情景.昔日,乡下有依次编号为1..n的n个小村庄, ...
- Hotel 旅馆, 线段树查询,合并
C. Hotel 旅馆 内存限制:256 MiB 时间限制:1000 ms 标准输入输出 题目类型:传统 评测方式:文本比较 题目描述 OIER最近的旅游计划,是到长春净月潭,享受那里的湖光山色, ...
- 记录21.08.04 — mybatis入门学习
mybatis入门 mybatis简介 MyBatis 是一款优秀的持久层框架,它支持自定义 SQL.存储过程以及高级映射.MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工 ...
- CMMI相关图书
Integrating CMMI and Agile Development: Case Studies and Proven Techniques for Faster Performance Im ...
- 《手把手教你》系列技巧篇(十八)-java+ selenium自动化测试-元素定位大法之By css中卷(详细教程)
1.简介 按计划今天宏哥继续讲解倚天剑-css的定位元素的方法:ID属性值定位.其他属性值定位和使用属性值的一部分定位(这个类似xpath的模糊定位). 2.常用定位方法(8种) (1)id(2)na ...
- Java Lambda 表达式源码分析
基本概念 Lambda 表达式 函数式接口 方法引用 深入实现原理 字节码 为什么不使用匿名内部类? invokedynamic 总结 参考链接 GitHub 项目 Lambda 表达式是什么?JVM ...