类型推断
类型检查机制包含: 类型推断 类型兼容性 类型保护
不需要指定变量类型或函数的返回值类型,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是否兼容接口Yx = y; // 源类型Y具备目标类型X的全部必要属性// 将变量x赋值给变量y, 测试接口Y是否兼容接口Xy = 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 不兼容 Point3Dp3d = 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;}

