typescript的interface多属性间如何约束?

rt,预期当品类species为Cat时,animalsType只能为Cat对应的string。
但实际上animalsType成为了联合类型

代码如下:

type SpeciesType = "Cat" | "Dog"
interface AnimalsType {
    Cat: String
    Dog: Number
    Bird: Boolean
}
interface Animals<T extends SpeciesType> {
    species: T
    animalsType: AnimalsType[T]
}


const animalList: Animals<SpeciesType>[] = [
    // ok
    {
        species: "Cat",
        animalsType: "1",
    },
    // ok
    {
        species: "Dog",
        animalsType: 1,
    },
    // expect:error!
    // 此处预期结果应该报错,希望从联合类型中取出Cat对应的为string
    {
        species: "Cat",
        animalsType: 1,
    },
    // 错误,此处不应该出现Bird类型
    {
        species: "Bird",
        animalsType: false,
    },
]

补充说明:
我已经了解到可以将Animals声明为联合类型来解决,但这个方案不适用于我的具体问题
示例如下:

type SpeciesType = "Cat" | "Dog"
interface AnimalsType {
    Cat: String
    Dog: Number
}
type Animals = {
    species: "Cat"
    animalsType: String
} | {
    species: "Dog"
    animalsType: Number
}

// 预期为:

const animalList: Animals[] = [
    // ok
    {
        species: "Cat",
        animalsType: "1",
    },
    // ok
    {
        species: "Dog",
        animalsType: 1,
    },
    // expect:error!
    // 此处预期结果应该报错,希望从联合类型中取出Cat对应的为string
    {
        species: "Cat",
        animalsType: 1,
    },
]

感觉这个类型可以转化为类型推断问题
将T具体推断为Cat/Dog,从而获取到对应的animalsType

阅读 1.5k
2 个回答
type Animals = {
  [K in keyof AnimalsType]: {
    species: K;
    animalsType: AnimalsType[K]
  }
}[keyof AnimalsType]
const animalList: Animals[] = [
    // ok
    {
        species: "Cat",
        animalsType: "1",
    },
    // ok
    {
        species: "Dog",
        animalsType: 1,
    },
    // error!
    {
        species: "Cat",
        animalsType: 1,
    },
]
type SpeciesType = 'Cat' | 'Dog'
interface AnimalsType {
  Cat: string;
  Dog: number;
  Bird: boolean;
}

type Animals<U> = U extends keyof AnimalsType ? { species: U; animalsType: AnimalsType[U] } : never

type Animal = Animals<SpeciesType>
/**
type Animal = {
    species: "Cat";
    animalsType: string;
} | {
    species: "Dog";
    animalsType: number;
}
*/

我理解你的需求是根据联合类型映射成 可辨识联合(Discriminated Unions),具体的映射方法为:

type A = 'one' | 'two' | 'three';
type Distribute<U> = U extends any ? {type: U} : never;

type B = Distribute<A>;

/*
type B = {
    type: "one";
} | {
    type: "two";
} | {
    type: "three";
}
*/

相关链接:
可辨识联合(Discriminated Unions)
联合类型映射为其他联合类型

推荐问题
logo
Microsoft
子站问答
访问
宣传栏