“财产类型'X'不兼容”但不是

时间:2017-07-05 16:06:26

标签: typescript

我尝试创建一个接口的对象,其属性从另一个对象初始化,如:

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 - 用常量初始化的相同变量)

  

具有相同类型的相同变量的两个行为!

1 个答案:

答案 0 :(得分:2)

虽然代码看起来在语义上是正确的,但编译器会不同意,因为Data不会保留有关下面类别的其他信息:它只知道字段typeCategory

因此,我们知道reference_ok可以分配为ReferenceOneReferenceTwoReferenceOne要求typeCategoryOne,而ReferenceTwo要求typeCategoryTwo。这些都不起作用,因为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进行同样的处理。