我有一个复杂的数据模型,我需要在其中定义 40-50 个单例“元数据”对象:
我希望能够保留为对象文字创建的隐式类型,以便我可以使用智能感知以类型安全的方式操作这 40-50 个对象。
这是一个使用高级类型打字稿文档中的形状的示例:
interface Square {
kind: "square";
size: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
interface Circle {
kind: "circle";
radius: number;
}
type Shape = Square | Rectangle | Circle;
type Shapes = { [x: string]: Shape };
interface Canvas {
name: string;
size: number;
//etc
}
interface Drawing extends Canvas {
shapes: Shapes;
}
//compiles but does not give me intellisense on drawingExplicitType.shapes because it is a Dictionary
let drawingExplicitType: Drawing = {
name: 'myDrawing',
size: 100,
shapes: {
c: { kind: "circle", radius: 1 },
r: { kind: "rectangle", width: 1, height: 1 },
}
};
//compiles and gives me intellisense on drawingImplicitType.shapes
let drawingImplicitType = {
name: 'myDrawing',
size: 100,
shapes: {
c: { kind: "circle", radius: 1 },
r: { kind: "rectangle", width: 1, height: 1 },
}
};
//will have many objects like drawingImplicitType (around 40-50) which I want to manipulate in a type safe manner
//also the shapes will have sub-shapes, so it's a tree-like data model
//I still want to be able to put all "drawing*" objects in an array of Drawing(s)
//ERROR!!! does not compile: Type 'string' is not assignable to type '"circle"'.
let drawing: Drawing = drawingImplicitType;
//## workaround ##########################################
class Drawing2 implements Canvas {
name: string;
size: number;
shapes: Shapes;
fromObjLiteral<T extends Pick<Drawing2, Exclude<keyof Drawing2, 'shapes' | 'fromObjLiteral'>> & {shapes: any}>(
obj: T & {shapes: {readonly [x in keyof T['shapes']]: Shape}}): Drawing2
{
Object.assign(this, obj);
return this;
}
}
//compiles and gives me intellisense on drawingImplicitType2.shapes
let drawingImplicitType2 = new (class {
name = 'myDrawing';
size = 100;
shapes = new (class {
c = new (class { readonly kind = "circle"; radius = 1 })();
r = new (class { readonly kind = "rectangle"; width = 1; height = 1 })();
})();
})();
let drawing2: Drawing2 = new Drawing2().fromObjLiteral(drawingImplicitType2);
let array: Drawing2[] = [drawing2];
//The workaround compiles and still allows me to make arrays of Drawing2(s)
//But I have to create new objects and the syntax is super ugly compared to:
//let drawingImplicitType2 = readonly {...obj literal here...}
Run Code Online (Sandbox Code Playgroud)
编辑 2018-05-25
正如Aleksey L.指出的那样,类型断言使用起来会更简单,但是我们需要在所有子对象上使用它们以获得强类型安全性。这对于递归树状数据模型Shape可能有点烦人,其中s 可以包含其他Shapes 但我认为它比new (class {...})()
//type assertions compile but allow me to make all kinds of errors:
let drawingTypeAssertion = {
name: 'myDrawing',
size: 100,
nonExistentField: 123,
shapes: {
c: { kind: "circle", radiusWrongField: 1 },
r: { kind: "rectangle", width: 1, height: 1 },
}
};
let drawingTA: Drawing = drawingImplicitType as Drawing;
//#####################################################################
// workaround 2: deep type assertions
//#####################################################################
let drawingDeepTypeAssertion = {
name: 'myDrawing',
size: 100,
nonExistentField: 123,
shapes: {
c: { kind: "circle", radius: 1 } as Shape,
r: { kind: "rectangle", width: 1, height: 1 } as Shape,
}
};
let drawingDTA: Drawing = drawingImplicitType as Drawing;
let array2: Drawing[] = [drawingDTA];
//This workaround compiles and still allows me to make arrays of Drawing2(s)
//no need to create extra objects and the syntax is a bit nicer than new (class {...})()
Run Code Online (Sandbox Code Playgroud)
结束编辑 2018-05-25
解决方法 2 的一大缺点是,一旦您这样做,as Shape您就会丢失 vscode 中的隐式类型和转到定义。
据我了解,问题来自这样一个事实,即对象文字是可变的,并且可区分的联合将不起作用。这主要是因为可变性引起的种类判别字段kind是string代替'square'或'rectangle'。
关于深度只读和“const”的讨论很多:https : //github.com/Microsoft/TypeScript/issues/10725 https://github.com/Microsoft/TypeScript/issues/15300
当前版本的打字稿是否有比上面更好的解决方法?
偶然发现类似的问题并找到了另一种解决方案:
从 typescript 3.5 开始,您还可以使用const 断言,然后:
let drawingTypeAssertion = {
name: 'myDrawing',
size: 100,
nonExistentField: 123,
shapes: {
c: { kind: "circle", radiusWrongField: 1 },
r: { kind: "rectangle", width: 1, height: 1 },
}
// Add this assertion here
} as const;
Run Code Online (Sandbox Code Playgroud)
这将使您的所有属性变为只读。
| 归档时间: |
|
| 查看次数: |
2493 次 |
| 最近记录: |