我正在为一个项目设置基础数据结构,希望拥有一个抽象的GraphNode
基本对象,让其他许多对象继承它。每个GraphNode子类都有元数据,可能包括字面量(字符串、数字等)和对其他GraphNode派生类型的引用。
示例子类:
interface IPerson {
name: string;
age: number;
friend: Person;
pet: Pet;
}
interface IPet {
type: 'dog' | 'cat';
name: string;
}
class Person extends GraphNode<IPerson> {}
class Pet extends GraphNode<IPet> {}
示例用法:
const spot = new Pet()
.set('type', 'dog')
.set('name', 'Spot');
const jo = new Person()
.set('name', 'Jo')
.set('age', 41)
.set('pet', spot) // ⚠️ Should not accept GraphNode argument.
.setRef('pet', spot); // ✅ Correct.
const sam = new Person()
.set('name', 'Sam')
.set('age', 45)
.set('friend', jo) // ⚠️ Should not accept GraphNode argument.
.setRef('friend', jo); // ✅ Correct.
我的问题是这样的——当GraphNode基类的通用类型在递归依赖于GraphNode类时,我该如何定义它?以下是我的尝试:
实用类型:
type Literal = null | boolean | number | string;
type LiteralAttributes<Base> = {
[Key in keyof Base]: Base[Key] extends Literal ? Base[Key] : never;
};
// ⚠️ ERROR — Generic type 'GraphNode<Attributes>' requires 1 type argument(s).
type RefAttributes<Base> = {
[Key in keyof Base]: Base[Key] extends infer Child
? Child extends GraphNode | null
? Child | null
: never
: never;
};
抽象图定义:
type GraphAttributes = {[key: string]: Literal | GraphNode | null};
abstract class GraphNode<Attributes extends GraphAttributes> {
public attributes = {} as LiteralAttributes<Attributes> | RefAttributes<Attributes>;
public get<K extends keyof LiteralAttributes<Attributes>>(key: K): Attributes[K] {
return this.attributes[key]; // ⚠️ Missing type information.
}
public set<K extends keyof LiteralAttributes<Attributes>>(key: K, value: Attributes[K]): this {
this.attributes[key] = value;
return this;
}
public getRef<K extends keyof RefAttributes<Attributes>>(key: K): Attributes[K] | null {
return this.attributes[key]; // ⚠️ Missing type information.
}
public setRef<K extends keyof RefAttributes<Attributes>>(key: K, value: Attributes[K] | null): this {
this.attributes[key] = value; // ⚠️ Missing type information.
return this;
}
}
那段代码无法编译,我在上面的注释中标记了TS错误。我认为这归结于一个基本问题-我想稍后扩展泛型类,使用引用泛型类的类型参数,但我不确定如何做到这一点。我可以容忍GraphNode类本身没有严格的类型检查,但我真的希望它的子类有严格的类型检查。
我还创建了一个 TypeScript playground 的示例, 该示例链接如下:
LiteralAttributes<T>
的一些属性的类型是never
?你希望attributes
保存never
类型的值吗?为什么attributes
是联合类型而不是交叉类型?你希望attributes
只保存A
的文字属性 或者 引用属性,而不是 同时 保存两者吗?我可能能够将其重构为编译通过的代码,但我希望这个示例能够更加简化。我会继续寻找。 - jcalzattributes
包含A的文字属性和A的引用属性的混合。我刚在TypeScript上找到了一个相关问题,并且根据我设置它的方式,它可能会出现错误。我不太担心null
与undefined
的区别,并且试图在这里概述它,因为我认为我稍后会知道如何解决它。 - Don McCurdy