我尝试创建一个接口的对象,其属性从另一个对象初始化,如:
id: data.reference.id
属性是兼容的,但typescript编译器会抛出错误。我不明白为什么以及如何避免这个错误。
(可以在之后找到测试代码的链接)
// INTERFACES AND TYPES
type CategoryOne = 1
type CategoryTwo = 2
type Category = CategoryOne | CategoryTwo
interface ReferenceOne {
id: string,
type: CategoryOne
}
interface ReferenceTwo {
id: string,
type: CategoryTwo
}
type Reference = ReferenceOne | ReferenceTwo
// DIRECT INIT
let reference_OK: Reference = { // works here
id: '1',
type: 1
}
// INIT FROM DATA
interface Data {
reference: {
id: string,
type: Category
}
}
let data: Data = {
reference: {
id: '1',
type: 1
}
}
let reference_KO: Reference = { // <--- error here
id: data.reference.id, // but compatible property
type: data.reference.type // but compatible property
}
let param: Category = data.reference.type // == 1
let reference_KO2: Reference = { // <--- error here
id: data.reference.id, // but compatible property
type: param // but compatible property
}
param = 1 // == data.reference.type, same variable
let reference_OK2: Reference = { // <--- no error here
id: data.reference.id,
type: param
}
this code in Typescript playground
[update] 我添加了一个从变量创建新引用的情况(reference_KO2 - 从数据属性初始化的变量 - 和reference_OK2 - 用常量初始化的相同变量)
具有相同类型的相同变量的两个行为!
答案 0 :(得分:2)
虽然代码看起来在语义上是正确的,但编译器会不同意,因为Data
不会保留有关下面类别的其他信息:它只知道字段type
是Category
因此,我们知道reference_ok
可以分配为ReferenceOne
或ReferenceTwo
。 ReferenceOne
要求type
为CategoryOne
,而ReferenceTwo
要求type
为CategoryTwo
。这些都不起作用,因为data.reference.type
是一般化的Category
。我们可以用更少的代码重现相同的效果,通过“向上转换”值:
let r: Reference = {
id: '1',
type: 1 as Category,
}
至少有两种方法可以避免这种情况。其中之一是重新定义Reference
作为接口,它基本上告诉编译器我们此时不需要在编译时知道类别。
interface Reference {
id: string,
type: Category,
}
然而,最吸引人的解决方案是使用泛型。我们可以扩充Data
以包含类别的类型:
interface Data<C extends Category> {
reference: {
id: string,
type: C
}
}
此时,此代码有效:
let data: Data<CategoryOne> = {
reference: {
id: '1',
type: 1
}
}
let reference_KO: Reference = {
id: data.reference.id,
type: data.reference.type
}
如果您愿意,也可以对Reference
进行同样的处理。