TS类型检查机制
作者:winter wang
更新于:11 天前
字数统计:1.6k 字
阅读时长:6 分钟
阅读量:
TS编译器在做类型检查时,所秉承的一些原则,以及表现出的一些行为。
作为:辅助开发,提高开发效率
- 类型推断
- 类型兼容性
- 类型保护
类型推断
不需要指定变量的类型(函数返回值类型),TS可以根据某些规则自动地为其推断出一个类型
- 基础类型推断
- 最佳通用类型推断
- 上下文类型推断
基础类型推断
ts
let a // any
const b = 1 // number
const c = [] // any[]
const d = [1] // number[]
- 声明变量的时候
- 设置函数默认参数
- 确定函数返回值
最佳通用类型推断
当有多个类型的时候,会推断成最佳通用的类型
ts
const b = [1, 'apple'] // (number | string)[]
上下文类型推断
从左到右推断,通常发生在事件处理中
ts
window.onkeydown = (event) => { // event: KeyboardEvent
console.log(event)
}
根据左侧的事件绑定,推断右侧的事件类型
类型断言
如果TS推断的类型不符合你的预期,可以使用类型断言覆盖TS的推断
ts
const foo = {}
foo.bar = 1
这样会报错,可以这样使用类型断言解决
ts
interface Foo {
bar: number
}
const foo = {} as Foo
foo.bar = 1
// 最好还是在声明的时候就指定类型
const foo1: Foo = {
bar: 1
}
类型兼容性
当一个类型Y可以被赋值给另一个类型X时,我们就可以说类型X
兼容类型Y
X兼容Y: X(目标类型)=Y(源类型),也就是说Y是X的子类型
设置 strictNullChecks: false
ts
let s = 'a'
s = null // 字符串变量可以被赋值给null
字符串类型是兼容null类型的,null是字符串类型的子类型
接口的兼容性
ts
interface X {
a: any
b: any
}
interface Y {
a: any
b: any
c: any
}
let x: X = { a: 1, b: 2 }
let y: Y = { a: 1, b: 2, c: 3 }
x = y // X类型可以兼容Y类型:鸭式辩型[源类型具备目标类型的必要属性;能多不能少]
y = x // Property 'c' is missing in type 'X' but required in type 'Y'.
接口之间相互兼容:成员
少
的会兼容成员多
的.
函数的兼容性
首先定义一个函数类型,再定义一个高阶函数,接受这个函数类型的函数
ts
type Handler = (a: number, b: number) => void
function hof(handler: Handler){
return handler
}
#### 参数个数
函数参数固定的时候
ts
const handler1 = (a: number) => {}
hof(handler1) // 可以少传
const handler2 = (a: number, b: number, c: number) => {}
hof(handler2) // Argument of type '(a: number, b: number, c: number) => void' is not assignable to parameter of type 'Handler'.
函数参数不固定的时候:可选参数与剩余参数
ts
let a = (x: number, y: number) => {}
let b = (x?: number, y?: number) => {}
let c = (...args: number[]) => {}
a = b
a = c
b = a // error
b = c // error
c = a
c = b
- 固定参数
兼容
可选参数与剩余参数 - 可选参数
不兼容
固定参数与剩余参数 (通过strictFunctionTypes: false
可以关闭) - 剩余参数
兼容
固定参数与可选参数
参数类型
ts
const handler3 = (a: string) => {}
hof(handler3) // error
如果函数参数类型是对象类型的呢?
ts
interface Point2D {
x: number
y: number
}
interface Point3D {
x: number
y: number
z: number
}
let p2d = (point: Point2D) => {}
let p3d = (point: Point3D) => {}
p3d = p2d
p2d = p3d // error (通过 `strictFunctionTypes: false` 可以关闭)
参数多的兼容参数少的
返回值类型
ts
let f1 = () => ({ x: 1 })
let f2 = () => ({ x: 1, y: 2 })
f1 = f2
f2 = f1 // error
与鸭式辩型法一样,成员少的兼容成员多的
函数重载
ts
function overload(a: number, b: number): number // 目标函数【参数多于源函数,类型小于源函数】
function overload(a: string, b: string): string // 目标函数
function overload(a: any, b: any): any {} // 具体实现:源函数
枚举的兼容性
数字和枚举可以相互兼容,枚举与枚举间不兼容
ts
enum Fruit { Apple, Banana }
enum Color { Red, Yellow }
let fruit: Fruit.Apple = 11
let num: number = Fruit.Apple
let color = enum: Color.Red = Fruit.Apple // 报错
类的兼容性
类的构造函数和静态成员不参与比较
ts
class A {
constructor(p: number, q: number) {}
id = 1
}
class B {
static s = 1
constructor(p: number) {}
id = 2
}
let aa = new A(1, 2)
let bb = new B(1)
aa = bb // 实例相互兼容
bb = aa // 实例相互兼容
当类中有私有成员时,两个类不兼容,类与子类可以
ts
class A {
constructor(p: number, q: number) {}
id = 1
private name = ''
}
class B {
static s = 1
constructor(p: number) {}
id = 2
private name = ''
}
let aa = new A(1, 2)
let bb = new B(1)
aa = bb // error
bb = aa // error
class C extends A {}
let cc = new C(1, 2)
aa = cc // 父类和子类的实例相互兼容
cc = aa // 父类和子类的实例相互兼容
泛型的兼容性
1)泛型接口 在两个泛型参数只有类型不相同时,只有在泛型参数使用时才影响
ts
interface Empty<T> {}
let obj1: Empty<number> = {}
let obj2: Empty<string> = {}
obj1 = obj2
obj2 = obj1
接口使用了类型参数,就会影响兼容性
ts
interface Empty<T> {
value: T
}
let obj1: Empty<number> = {}
let obj2: Empty<string> = {}
obj1 = obj2 // error
obj2 = obj1 // error
2)泛型函数
ts
let log1 = function<T>(x: T): T {
console.log('x')
return x
}
let log2 = function<U>(y: U): U {
console.log('y')
return y
}
log1 = log2
log2 = log1
总结
结构之间进行比较:成员少的兼容成员多的(我要的你都有,那我兼容你) 函数之间进行比较:参数多的兼容参数少的(你给我太多我处理不了,我不能兼容你)
类型保护
TS能够在特定的区块中保证变量属于某种确定的类型,可以在区块中放心的引用此类型的属性,或者调用此类型的方法
ts
class Java {
helloJava() {
console.log('hello Java')
}
java: any
}
class JavaScript {
helloJavaScript() {
console.log('hello JavaScript')
}
javascript: any
}
function getLanguage(type: Type) {
const lang = type === Type.Strong ? new Java() : new JavaScript()
// TODO
return lang
}
getLanguage(Type.Strong)
instanceof
判断一个实例是否属于某个类
ts
function getLanguage(type: Type){
let lang = type === Type.Strong ? new Java() : new JavaScript()
// TODO
if(lang instancof Java){
lang.helloJava()
}else {
lang.helloJavaScript()
}
return lang
}
in
判断对象中是否包含某个属性
ts
function getLanguage(type: Type) {
const lang = type === Type.Strong ? new Java() : new JavaScript()
// TODO
if ('java' in lang)
lang.helloJava()
else
lang.helloJavaScript()
return lang
}
typeof
ts
function test(x: string | number) {
if (typeof x === 'string')
x.length
else
x.toFixed(2)
}
创建类型保护函数
类型谓词 lang is Java
ts
function isJava(lang: Java | JavaScript): lang is Java {
return (lang as Java).helloJava !== undefined
}
// 创建类型保护区块
if (isJava(lang))
lang.helloJava()
else
lang.helloJavaScript()