Eslint
Bug 变量传递 class
通过将一个 class 传递给一个变量,该变量无法被 eslint 识别为 class:
https://github.com/typescript-eslint/typescript-eslint/issues/1485
Typescript
Intersection type
同名冲突
interface 更偏向继承的思想
Interfaces vs. Intersections We just looked at two ways to combine types which are similar, but are actually subtly different. With interfaces, we could use an extends clause to extend from other types, and we were able to do something similar with intersections and name the result with a type alias. The principle difference between the two is how conflicts are handled, and that difference is typically one of the main reasons why you’d pick one over the other between an interface and a type alias of an intersection type.
interface 同名冲突表现为 覆盖
虽然会覆盖,但是 ts 仍旧会提示
也就是 ts 会检查 interface 时同名属性的 subtyping 关系
interface One {
name: string
}
interface Two extends One{
name: number
}
const testa: Two = {
name: '11', // number
}
type Intersections 同名冲突表现为 合并
如这里的 string 与 number,由于不存在既是 number 又是 string 的情况,所以最终为 never
type One1 = {
name: string;
}
type Two1 = {
name: number;
}
const testa1: One1 & Two1 = {
name: '11', // never
};
对于 OOP,能使用 interface 的场景,尽量使用 interface,因为 type 存在合并出错的可能。
由于 interface 只可用于抽象定义,所以对一个 接口 的 实现 还是要通过 class 来进行,如这里的 DefaultEngine 则代表了对 Engine 这一 接口 的 默认实现:
interface Engine {}
// 如需要默认实现,使用 DefaultXX class 来做最就基本的接口实现
class DefaultEngine implements Engine {}
// 但由于 Ts 类不支持多重继承,所以 细粒度化 的 多接口组合,还是得先 抽象组合 后,再进行 基本实现。
// 不是很好用
对于 OOP 来说,接口的存在意义就是为了解耦,且接口定义的内容越少越好。
开发中错误的做法就是 从实现类中提取接口(或直接继承实现类来获得某些功能),应该 先定义服务方与消费方的接口,再进行实现。在未使用(需要实现)时,只做抽象接口的组合(interface 互相 extends 实现)。
另外,最好的 非继承组合方式,应该是装饰器,但至今 Ts 无法解决装饰器的类型检查:
https://github.com/microsoft/TypeScript/issues/4881
如果以下这段代码正常,世界将变得美好起来
interface HasNewProperty {
newProperty: string;
}
function classDecorator<T extends { new (...args: any[]): {} }>(
constructor: T,
) {
return class extends constructor implements HasNewProperty {
newProperty = 'new property';
hello = 'override';
};
}
@classDecorator
class Greeter {
property = 'property';
hello: string;
constructor(m: string) {
this.hello = m;
}
}
console.log(new Greeter('world'));
// Alas, this line makes the compiler angry because it doesn't know
// that Greeter now implements HasNewProperty
console.log(new Greeter('world').newProperty);
Type
typescript 的函数重载
ts 的函数重载属于 Intersection type(交叉),不是 union type(联合)。也就是 几个函数的合体,而不是 几个函数中的一个。
因为 Typescript 始终是要编译成 js的,函数重载并不是真正的函数重载入(有多个函数),实际只有一个函数,所以是几个函数的合体。至于为什么要这么实现可以参考 Ts 官方的语言目标:
https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals#goals
所以其实如果觉得重载的写法容易迷惑,可以采用简单的 Intersection type 也就是 xx & xx 的写法来代替。
_
另外,interface 的多函数写法,实际就是一个 intersection type ,感觉更合适的函数重载写法
interface A {
(arg: number): number;
(arg: string): string;
}
可以简单验证:
但是该方式会导致函数体本身的自查跳过,可能会出现问题,应该尽量避开。
interface A {
(arg: number): number;
(arg: string): string;
}
// type A = ((arg: number) => number) | ((arg: string) => string);
const a = function (arg: number | string) {
if (typeof arg === 'string') return arg;
return arg;
} as A; // !注意这是一个特殊写法 丧失函数体内部自查,不安全
a({})
重载生效了:
自查失效了:
所以如果函数体参数复杂,返回值复杂的场景,还是老老实实采用 ts 的重载入写法吧,虽然繁琐