细说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 这种大的战略野心, ...
随机推荐
- centos7下安装、配置Nginx、设置Nginx开机自启动
测试环境: [root@centos-linux ~]# cat /etc/redhat-releaseCentOS Linux release 7.6.1810 (Core) [root@cento ...
- 【洛谷P1318积水面积】最小生成树
我写一篇绝对原创的题解,算法原创,求洛谷通过!!!(让更多人看到这篇题解) 绝大多数人肯定认为这道题是一道模拟题 以下为正解 我们来看一下这一道题,其实就是找到左右高点,在模拟. 但是这个是正常人的想 ...
- Mysql数据量较大时分页查询优化
据表 collect ( id, title ,info ,vtype) 就这4个字段,其中 title 用定长,info 用text, id 是主键,vtype是int,vtype是索引. 最后co ...
- webpack 安装使用简例
1. 新建项目文件夹,如 webpack-demo 2. cd 到安装项目根目录,即进入webpack-demo文件夹,命令行工具输入"npm install webpack webpack ...
- 福昕foxit phantom pdf高级编辑器企业版10.1 pro安装破解教程
本文提供福昕foxit phantom pdf高级编辑器企业版10.1的安装教程.pj教程,可以使用全部功能,注意的是此方法对个人版无效. 没有必要再尝试别的文章,仅看这一篇即可!别的文章亲测是通过修 ...
- 工业互联网之微信小程序整体解决方案总结
随着工业互联网的快速发展,以及微信小程序的成熟,将两者结合实现对工况数据的查看和存储方案变得简单.以下方式为本人尝试过的整体解决方案,可以顺利实现无缝对接. 1.采集方式 1.ifix/intouch ...
- phpmyadmin error:#2002 - 服务器没有响应 (或者本地 MySQL 服务器的套接字没有正确配置)
1. 将 "phpMyAdmin/libraries"文件夹下的config.default.php文件中的$cfg['Servers'][$i]['host'] = 'local ...
- git命令行or图形化界面?看这篇操作就够了
我们在自己的私人分支开发好各自的模块后,就要合并到master,这个时候在idea里边先切换到master,然后update一下获取最新更新,解决一下冲突,最后再合并自己的私人分支,add,commi ...
- Python语言系列-07-面向对象2
重构父类__init__方法 #!/usr/bin/env python3 # author:Alnk(李成果) # 需求:Dog类要新增一个实例属性,但是Cat类不需要 class Animal(o ...
- CleanArchitecture Application代码生成插件-让程序员告别CURD Ctrl+C Ctrl+V
这是一个根据Domain项目中定义的实体对象(Entity)生成符合Clean Architecture原则的Application项目所需要的功能代码,包括常用的Commands,Queries,V ...