类型推断
类型检查机制包含: 类型推断 类型兼容性 类型保护
不需要指定变量类型或函数的返回值类型,ts 可自动推断出类型
类型推断包括: 基础类型推断 最佳通用类型推断 上下文类型推断
typescript
// 基础类型推断
// 最佳通用类型推断
let a = 1
let b = [1]
let b1 = [1, null]
let c = (x = 1) => {}
let c1 = (x = 1) => x + 1
// 上下文推断
// window.onkeydown = (event) => { // KeyboardEvent
// console.log(event.button);
// };
类型断言
typescript
interface Foo {
bar: number
}
let foo = {} as Foo
foo.bar = 1
注意: 类型断言可以将某一变量特指为某种类型,但并不会产生接口约束作用,如下举例
typescript
interface Foo {
bar: number
}
let foo = {} as Foo
// foo.bar = 1 不进行赋值
foo 被指定为 Foo 接口类型,但真实的 foo 是一个空对象,没有 foo.bar =1相当于没有 bar 属性,所以此时的接口并不能起到约束作用。 解决: 直接在声明的时将对象指定为接口类型,而不使用类型断言
typescript
{
interface Foo {
bar: number
}
let foo: Foo = {}
}
{
interface Foo {
bar: number
}
// let foo = {} as Foo;
// foo.bar = 1;
let foo: Foo = {
bar: 1,
}
}
各种类型之间的兼容性
tsconfig.json => "strictNullChecks": false,(个人不推荐)
typescript
/**
* X 兼容 Y: X (目标类型) = Y (源类型)
*/
let s: string = 'a'
s = null
接口兼容性
接口类型相当于对变量属性的约束,必须全部满足才可以兼容赋值
typescript
// 接口兼容性
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: a, b: 2, c: 3 }
// 将变量y赋值给变量x, 测试接口X是否兼容接口Y
x = y // 源类型Y具备目标类型X的全部必要属性
// 将变量x赋值给变量y, 测试接口Y是否兼容接口X
y = x // 源类型X不具备目标类型Y的全部必要属性(不含c属性)
函数的兼容性
目标函数能够兼容源函数,需同时满足以下三个条件: 参数的个数(目标函数的参数个数一定要多于源函数的参数个数) 参数的类型(参数类型必须匹配) 返回值的类型(标函数的返回值类型 与 原函数的返回值类型 必须相同或为其子类型)
typescript
// 函数兼容性
type Handler = (a: number, b: number) => void
function hof(hadler: Handler) {
return hadler
}
函数兼容性的影响因素
参数个数
typescript
let hadler1 = (a: number) => {} // 少参数
hof(hadler1)
let hadler2 = (a: number, b: number, c: number) => {} // 多参数
hof(hadler2)
// 当参数不固定时,固定参数与可选参数,剩余参数的兼容性
{
// 可选参数和剩余参数
let a = (p1: number, p2: number) => {}
let b = (p1?: number, p2?: number) => {}
let c = (...args: number[]) => {}
// tsconfig.json -> "strictFunctionTypes": false,
a = b // 固定参数可以兼容可选参数
a = c // 固定参数可以兼容剩余参数
// tsconfig.json ("strictFunctionTypes": false可兼容)
// b = c; // 可选参数不兼容固定参数
// b = a; // 可选参数不兼容剩余参数
c = a // 剩余参数不兼容固定参数
c = b // 可选参数不兼容剩余参数
}
参数类型
基础类型
typescript
// 函数兼容性
type Handler = (a: number, b: number) => void
function hof(hadler: Handler) {
return hadler
}
let hadler3 = (a: number) => {}
hof(hadler3)
// let hadler3 = (a: string) => { }
// hof(hadler3)
对象类型
typescript
interface Point3D {
x: number
y: number
z: number
}
interface Point2D {
x: number
y: number
}
let p2d = (ponit: Point2D) => {}
let p3d = (ponit: Point3D) => {}
// tsconfig.json ("strictFunctionTypes": false可兼容)
p2d = p3d // Point2D 不兼容 Point3D
p3d = p2d // Point3D 兼容 Point2D
返回值类型
typescript
let f1 = () => ({ name: 'Alice' })
let g = () => ({ name: 'Alice', location: 'Beijing' })
// 成员少的兼容成员多的
f1 = g
g = f1
小技巧: > 返回值类型,和接口类似相当于是对函数的一种约束,所以必须在满足要求的前提先才可以兼容 > 即参数可以多了我不用,但绝对不可以少
函数重载对函数兼容性的影响
typescript
// 函数定义-重载列表
function overload(a: number, b: number): number
function overload(a: number, b: number): string
// 函数实现
// 兼容: 目标函数参数个数 > 源函数参数个数
function overload(a: any, b: any): any {}
// 不兼容: 目标函数参数个数 < 源函数参数个数
// function overload(a: any, b: any, c: any): any {}
// 不兼容: 删除返回值
// function overload(a: any, b: any) {}
枚举类型的兼容性
typescript
// 枚举兼容性
enum Fruit {
Apple,
Banana,
}
enum Color {
Red,
Yellow,
}
let fruit: Fruit.Apple = 1
let no: number = Fruit.Apple
// 不同枚举之间不兼容
// let color: Color.Red = Fruit.Apple;
类的兼容性
typescript
// 类的兼容性
// 静态成员和构造函数不参与类的兼容性比较
class A {
constructor(p: number, q: number) {} // 构造函数
id: number = 1
private name: string = '' // 私有成员
}
class B {
static s: number = 1 // 静态成员
constructor(p: number) {} // 构造函数
id: number = 2
private name: string = '' // 私有成员
}
let aa = new A(1, 2)
let bb = new B(1)
// 类中包含私有成员,两个类不兼容
// aa = bb;
// bb = aa;
// 但父类和子类之间兼容
class C1 extends A {} // 创建子类
let cc = new C1(1, 2)
aa = cc // 父类兼容子类
cc = aa // 子类兼容父类
泛型的兼容性
typescript
// 当类型参数T未被成员使用是,不会影响泛型兼容性
interface Empty<T> {}
let obj1: Empty<number> = {}
let obj2: Empty<string> = {}
obj1 = obj2
// 当类型参数T被成员使用时,才会影响泛型兼容性
interface Empty1<T> {
attr: T
}
let obj3: Empty1<number> = { attr: 1 }
let obj4: Empty1<string> = { attr: '1' }
// obj3 = obj4;
// 泛型函数的兼容性
// 定义相同的两个泛型函数,如果未指定具体类型,则可相互兼容
let t1 = <T>(x: T): T => {
console.log('x')
return x
}
let t2 = <U>(y: U): U => {
console.log('y')
return y
}
t1 = t2 // 未指定类型的两个定义相同的泛型函数,可相互兼容
结构之间兼容:成员少的兼容成员多的 函数之间兼容:参数多的兼容参数少的
类型保护机制
typescript
enum Type {
Strong,
Week,
}
class Java {
helloJava() {
console.log('Hello Java')
}
java: any // 新增java属性
}
class JavaScript {
helloJavaScript() {
console.log('Hello JavaScript')
}
javascript: any // 新增javascript属性
}
function getLanguage(type: Type) {
let lang = type === Type.Strong ? new Java() : new JavaScript()
return lang
}
getLanguage(Type.Strong)
TS不能准确判断实例到底是哪一种类型,因为TS不能准确判断实例到底是哪一种类型
使用类型断言
typescript
function getLanguage(type: Type) {
let lang = type === Type.Strong ? new Java() : new JavaScript()
if ((lang as Java).helloJava) {
;(lang as Java).helloJava()
} else {
;(lang as JavaScript).helloJavaScript()
}
return lang
}
创建类型保护区块
使用instance关键字创建区块
typescript
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()
}
return lang
}
通过in关键字判断变量所属类型来创建区块
typescript
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()
}
return lang
}
通过typeof对参数x的类型进行判断,创建对应的区块
typescript
function getLanguage(type: Type, x: string | number) {
let lang = type === Type.Strong ? new Java() : new JavaScript()
if (typeof x === 'string') {
x.length // string
} else {
x.toFixed(2) // number
}
return lang
}
使用类型保护函数
typescript
function getLanguage(type: Type, x: string | number) {
let lang = type === Type.Strong ? new Java() : new JavaScript()
if (isJava(lang)) {
lang.helloJava
} else {
lang.helloJavaScript
}
return lang
}