类型推断
类型检查机制包含: 类型推断 类型兼容性 类型保护
不需要指定变量类型或函数的返回值类型,ts 可自动推断出类型
类型推断包括: 基础类型推断 最佳通用类型推断 上下文类型推断
// 基础类型推断
// 最佳通用类型推断
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);
// };
类型断言
interface Foo {
bar: number;
}
let foo = {} as Foo;
foo.bar = 1;
注意: 类型断言可以将某一变量特指为某种类型,但并不会产生接口约束作用,如下举例
interface Foo {
bar: number;
}
let foo = {} as Foo;
// foo.bar = 1 不进行赋值
foo 被指定为 Foo 接口类型,但真实的 foo 是一个空对象,没有 foo.bar =1相当于没有 bar 属性,所以此时的接口并不能起到约束作用。 解决: 直接在声明的时将对象指定为接口类型,而不使用类型断言
{
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,(个人不推荐)
/**
* X 兼容 Y: X (目标类型) = Y (源类型)
*/
let s: string = "a";
s = null;
接口兼容性
接口类型相当于对变量属性的约束,必须全部满足才可以兼容赋值
// 接口兼容性
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属性)
函数的兼容性
目标函数能够兼容源函数,需同时满足以下三个条件: 参数的个数(目标函数的参数个数一定要多于源函数的参数个数) 参数的类型(参数类型必须匹配) 返回值的类型(标函数的返回值类型 与 原函数的返回值类型 必须相同或为其子类型)
// 函数兼容性
type Handler = (a: number, b: number) => void;
function hof(hadler: Handler) {
return hadler;
}
函数兼容性的影响因素
参数个数
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; // 可选参数不兼容剩余参数
}
参数类型
基础类型
// 函数兼容性
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)
对象类型
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
返回值类型
let f1 = () => ({ name: "Alice" });
let g = () => ({ name: "Alice", location: "Beijing" });
// 成员少的兼容成员多的
f1 = g;
g = f1;
小技巧: 返回值类型,和接口类似相当于是对函数的一种约束,所以必须在满足要求的前提先才可以兼容 即参数可以多了我不用,但绝对不可以少
函数重载对函数兼容性的影响
// 函数定义-重载列表
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) {}
枚举类型的兼容性
// 枚举兼容性
enum Fruit {
Apple,
Banana,
}
enum Color {
Red,
Yellow,
}
let fruit: Fruit.Apple = 1;
let no: number = Fruit.Apple;
// 不同枚举之间不兼容
// let color: Color.Red = Fruit.Apple;
类的兼容性
// 类的兼容性
// 静态成员和构造函数不参与类的兼容性比较
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; // 子类兼容父类
泛型的兼容性
// 当类型参数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; // 未指定类型的两个定义相同的泛型函数,可相互兼容
结构之间兼容:成员少的兼容成员多的 函数之间兼容:参数多的兼容参数少的
类型保护机制
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不能准确判断实例到底是哪一种类型
使用类型断言
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关键字创建区块
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关键字判断变量所属类型来创建区块
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的类型进行判断,创建对应的区块
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;
}
使用类型保护函数
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;
}