ts的类型检查

一.类型推论

1.通用类型(从右到左推断):
TypeScript里的类型推论。即,类型是在哪里如何被推断的。

在有些没有明确指出类型的地方,类型推论会帮助提供类型,如下面的例子中的a,b,c
变量a被推断为number,b被推断为any[],c被推断为strng[],
当需要从几个表达式中推断类型时候,会使用这些表达式的类型来推断出一个最合适的通用类型,例如下例中的x
由于x有多个类型,所以x被推断为number|null.
设置函数默认参数或者确定函数返回值的时候,也可以做出推断出,如下例中的xx.
有明确的number参数,和string返回值

2.上下文类型(从左到右推断):
类型推论也可能按照相反的方向进行。按上下文归类会发生在表达式的类型与所处的位置相关时。比如:

window.onmousedown = function(mouseEvent) {
    console.log(mouseEvent.button);  //<- Error
};
/*
上面这个例子会得到一个类型错误,TypeScript类型检查器使用Window.onmousedown函数的类型来推断右边函数表
达式的类型。 因此,就能推断出 mouseEvent参数的类型了。 如果函数表达式不是在上下文类型的位置, 
mouseEvent参数的类型需要指定为any,这样也不会报错了,如下
*/

window.onmousedown = function(mouseEvent: any) {
    console.log(mouseEvent.button);  //<- Now, no error is given
};

二.类型断言

// 定义一个空对象,然后给空对象添加一个button属性
let dy = {};
dy.button = 'no btn'; // 报错: 类型“{}”上不存在属性“button”。

// 我们看到报错了,
// 那么怎么解决这个报错呢,这时候我们可以写一个接口,让接口有一个button属性,然后使用断言改写上面的代码

interface Dy {
    button:string
}
let dy = {} as Dy
dy.button = 'no btn';

// 但是这样写又一个弊端,不能束缚dy里面一定存在button,
// 所以在不确定的情况下,少用这种断言,那么我们要怎么写呢,在声明的时候就指定类型

let dy:Dy = {button:''}; // 确保dy里面一定受Dy的束缚
dy.button = 'no btn';

三.类型兼容

结构之间的兼容: 成员少的兼容成员多的
函数之间的兼容:参数多的兼容参数少的

什么是类型兼容?
当一个类型y可以被赋值给类型x时,我们可以说类型x兼容类型y
x兼容y: x(目标类型) = y(源类型)

接口兼容

interface XX {
    a: number;
    b: number
}
interface YY {
    a: number;
    b: number;
    c: number
}
var x: XX = { a: 1, b: 2 };
var y: YY = { a: 5, b: 6, c: 3 }
x = y; // 正确
y = x; //报错:  “c”需要在这里声明。

这是什么原理呢,因为y对于x来说,y满足了x的必需属性,但是x对y来说缺少了c这个必需属性,这个被称做“鸭式辨型法”或“结构性子类型化”, 所以x可以兼容y,而y并不兼容x,总结来说就是:成员少的会兼容成员多的

函数兼容

查看两个函数是否兼容,通常看两个函数是否相互赋值

函数作为参数的情况下

type handler = (x:number,y:number) => void; 
function fa(handler){
    return handler;
}

//参数个数:目标函数的参数个数一定要多于原函数的参数个数
let heanler1 = (x: number) => { };
fa(heanler1); // 正确

let heanler2 = (x: number,y:number,z:number) => { };
fa(heanler2); // 报错

可选参数和剩余参数之间的兼容性

固定参数可以兼容可选参数和剩余参数
可选参数不兼容固定参数和剩余参数

扫描二维码关注公众号,回复: 13158534 查看本文章
let a = (a:number,b:number)=>{};
let b = (a?:number,b?:number)=>{};
let c = (...arg:number[])=>{};
// 固定参数可以兼容可选参数和剩余参数即
a=b;a=c; // 正确

// 可选参数不兼容固定参数和剩余参数
// 设置"strictFunctionTypes": false,就可以跳过下面的报错
b=a;// 报错
b=c;// 报错

参数类型

参数成员多兼容参数成员少的,跟接口兼容正好相反

let handler3 = (x:string)=>{}
fa(handler3); // 参数类型错误的话也会报错
// 对象参数
interface Point3D{
    x:number;
    y:number;
    z:number;
}
interface Point2D{
    x:number;
    y:number;
}

let p3d = (p:Point3D) => {}
let p2d = (p:Point2D) => {}

// 参数成员多兼容参数成员少的,跟接口兼容正好相反
p3d = p2d;
p2d = p3d; // 报错

返回值类型

返回值成员少的兼容返回值成员多的

let h = ()=> ({name:'张三'})
let g = ()=> ({name:'张三',age:'30'})
h=g;
g=h; // 报错   {name:'张三'}不能分配给{name:'张三',age:'30'}

函数重载的兼容性

对于有重载的函数,源函数的每个重载都要在目标函数上找到对应的函数签名。 这确保了目标函数可以在所有源函数可调用的地方调用。

目标函数的参数,要多于原函数的参数,并且返回值的类型也要符合相应的要求

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):{}; // 报错
// function overload(a:any,b:any,c:any):any{}; // 报错

枚举的兼容性

枚举类型与数字类型兼容,并且数字类型与枚举类型兼容。不同枚举类型之间是不兼容的。比如,

enum Status { Ready, Waiting };
enum Color { Red, Blue, Green };

let st = Status.Ready; // st:Status.Ready

// 枚举和数字之间是完全兼容的
st = 2;  // 正确  
let st1:

// 枚举之间是不兼容的
st = Color.Green;  // 报错  
st = Status.Waiting;  // 报错

类的兼容性

类与对象字面量和接口差不多,但有一点不同:类有静态部分和实例部分的类型。 比较两个类类型的对象时,只有实例的成员会被比较。 静态成员和构造函数不在比较的范围内。如果类中存在私有成员,则不兼容

类的私有成员和受保护成员
类的私有成员和受保护成员会影响兼容性。 当检查类实例的兼容时,如果目标类型包含一个私有成员,那么源类型必须包含来自同一个类的这个私有成员。 同样地,这条规则也适用于包含受保护成员实例的类型检查。 这允许子类赋值给父类,但是不能赋值给其它有同样类型的类。

class Animal {
    feet: number;
    constructor(name: string, numFeet: number) { }
}
class Size {
    feet: number;
    constructor(numFeet: number) { }
}
let a: Animal;
let s: Size;
a = s;  // OK
s = a;  // OK

// 还是上面的代码,Animal 添加私有成员
class Animal {
    ...
    private age: number; // 添加私有成员后
}
...
a = s;  // Error
s = a;  // OK

// 继给Animal添加私有成员后,再给Size添加私有成员
class Size {
     ...
     private age: number; // Size 添加私有成员后
}
...
a = s;  // Error
s = a;  // Error

// 允许子类赋值给父类
class Dog extends Animal { }
let c = new Dog('哈士奇', 20);
c = a; // OK
a = c; // OK

泛型的兼容性

因为TypeScript是结构性的类型系统,类型参数只影响使用其做为类型一部分的结果类型。比如,

interface Empty<T> {
}
let x: Empty<number>;
let y: Empty<string>;

x = y;  // OK, 因为y符合x的结构

上面代码里,xy是兼容的,因为它们的结构使用类型参数时并没有什么不同。 把这个例子改变一下,增加一个成员,就能看出是如何工作的了

// 当T被使用的时候,就会报错
interface NotEmpty<T> {
    data: T;
}
let x: NotEmpty<number>;
let y: NotEmpty<string>;

x = y;  // Error, 因为x和y不兼容

如果两个泛型函数定义相同,并且没有指定类型参数,那么他们是可以相互兼容的,例如

let identity = function <T>(x: T): T {
    console.log(x)
    return x;
}

let reverse = function <U>(y: U): U {
    console.log(y)
   return y;
}
identity = reverse;  // OK, because (x: any) => any matches (y: any) => any

而下面的代码则是会报错

let identity = function <T>(x: T): T {
    console.log(x)
    return x;
}

let reverse = function <U,T>(y: U,z:T): U {
    console.log(y)
   return y;
}

identity = reverse;  // Error

猜你喜欢

转载自blog.csdn.net/l284969634/article/details/105223085
今日推荐