细说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 这种大的战略野心, ...
随机推荐
- windows下搭建vue开发环境实践
Vue.js是一套构建用户界面的 "渐进式框架".与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计.Vue 的核心库只关注视图层,并且非常容易学习,非常容易与其它库或已 ...
- vue(24)网络请求模块axios使用
什么是axios Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中. 主要的作用:axios主要是用于向后台发起请求的,还有在请求中做更多是可控功能. a ...
- 每天五分钟Go - Map
map的定义 var m map[type]type fmt.Println(m) 此种方法定义的m为nil //打印的结果为: map[] map的创建 1.使用make创建 var m1 = ma ...
- js之 foreach, map, every, some
js中array有四个方法 foreach, map, every, some,其使用各有倾向. 关注点一:foreach 和 map 无法跳出循环,每个元素均执行 foreach 和 map 无法跳 ...
- VM12升级VM15
之前一直用的12,现在想要升级为15.主要是为了解决kali操作系统版本兼容问题 打开VM12,点击[帮助]-->[软件更新]--> [检查更新] 发现有VM15,点击[了解详情] VM1 ...
- C#制作网盘搜索工具(简单的爬虫)
最近学习C#编程,在网上发现一篇winform下制作百度网盘搜索器的文章,故而下载源码学习一二.无奈原博所用的网址失效,故而自己改写了网址和相关源代码,也进行了实现.因为初学,接触的知识较多,为免忘记 ...
- SAS启动时自动执行代码
有时候我们希望SAS启动时自动执行已经编写好的程序,可以按照以下方法实现: 首先正常打开SAS,编写我们想要让SAS启动时自动执行的代码,例如获取桌面文件夹路径,以便在其他程序中引用这个路径. pro ...
- Apache Flink jobmanager/logs路径遍历CVE-2020-17519
影响版本 1.11.0 1.11.1 1.11.2 poc http://192.168.49.2:8081/jobmanager/logs/..%252f..%252f..%252f..%252f. ...
- DC-2 靶机渗透测试
DC-2 靶机渗透测试 冲冲冲,好好学习. 本靶机核心内容"受限shell提权",知识点在另一篇文章中总结归纳了. 攻击机:kali 靶 机:DC-2 准备:在使用前需要在操作机的 ...
- 查看Android 系统发送的广播
命令行输入如下命令 adb shell dumpsys |grep BroadcastRecord