Typescript的结构类型系统是什么?
Anders Hejlsberg先后设计了C#和Typescript, 两者有很多相似之处,但是其本质区别是
C#采用的 标明类型系统
TS 采用的是结构类型系统
public class Foo
{
public string Name { get; set; }
public int Id { get; set;}
}
public class Bar
{
public string Name { get; set; }
public int Id { get;set }
}
Foo foo = new Foo(); // Okay
Bar bar = new Foo(); // Error!!!
即使Foo和Bar两个类的内部定义完全一致,但是赋值是会报错,可见两者类型本质是不同的
class Foo {
method(input: string):number {}
}
class Bar {
method(input: string):number {}
}
const foo:Foo = new Foo() // Okay
const bar:Bar = new Foo() // Okay
TS只要两者类型相同,即可以赋值,本质原因是TS比较的并不是类型定义本身,而是类型定义的形状
TS采用结构类型系统,是为了兼容js灵活的特性
- 如何防止两种类型在结构上兼容
由于TS采用了灵活的结构类型系统,那么会导致一些问题
interface ScreenCoordinate {
x: number;
y: number;
}
interface PrintCoordinate {
x: number;
y: number;
}
function sendToPrinter(pt: PrintCoordinate) {
// .....
}
function getCursorPos(): ScreenCoordinate {
return { x:0, y:0 }
}
sendToPrinter(getCursorPos())
由于ScreenCoordinate和PrintCoordinate的形状是相同的,那么根据结构类型系统的特性,它们的类型是兼容的,
但是如果不想让他们是兼容的类型如何操作?
添加一个[brand]成员:
interface ScreenCoordinate {
_screenCoordBrand: any;
x: number;
y: number;
}
interface PrintCoordinate {
_printCoordBrand: any;
x: number;
y: number;
}
// 报错
sendToPrinter(getCursorPos());
- TS类型的substitutability?
TS支持更少的参数的函数可以赋值给更多的参数的函数, 究其原因是因为handler类型 (arg: string)=> xxx 是可以作为(arg1: string, arg2: number) => void 的替代品,在这种情况下是不会报错的function handler(arg: string) {
//...
}
function doSomething(callback: (arg1: string, arg2: number) => void) {
callback('hello', 42)
}
doSomething(handler)
// 一般预期这里会报错,因为doSomething要求的回调函数式有两个参数的,
// 但是handlerz只有一个参数
类似的例子还有 ```typescript function doSomething(): number { return 42 } function callMeMaybe(callback: () => void) { callback() }
// 一般认为这里会报错,原因是doSomething返回的是number, 而callback返回的是void; callMeMayBe(doSomething) ``` TS有substitutability的设计,原因也是要兼容JS的灵活性
小结
- TS的类型系统,是结构型类型系统,即结构相同则认为类型相同
- TS防止两种类型在结构上兼容,可以通过给结构相同的接口添加 [band ]成员解决
- TS的substituability的设计,参数更少的函数可以赋值给参数更多的函数,即使类型不相同