简体   繁体   English

为什么映射类型不能使用通用但有限的键完全解析查找?

[英]Why can’t a mapped type fully resolve a lookup using a generic, but finite, key?

Here's a reduction of the problem:这是问题的减少:

type Animal = 'dog' | 'cat';

type AnimalSound<T extends Animal> = T extends 'dog'
    ? 'woof'
    : T extends 'cat'
    ? 'meow'
    : never;

const animalSoundMap: {[K in Animal]: AnimalSound<K>} = {
    dog: 'woof',
    cat: 'meow',
};

const lookupSound = <T extends Animal>(animal: T): AnimalSound<T> => {
    const sound = animalSoundMap[animal];
    return sound; 
}

Playground link 游乐场链接

The return line is an error; return行是一个错误; the error message suggests that the sound variable is resolved to 'woof' | 'meow'错误消息表明sound变量被解析为'woof' | 'meow' 'woof' | 'meow' , even though it seems like TS should be able to type it as AnimalSound<T> based on the type of animalSoundMap . 'woof' | 'meow' ,尽管它看起来像TS应该能够键入它作为AnimalSound<T>基于所述类型animalSoundMap So why doesn't the typechecker like it?那么为什么打字员不喜欢它呢?

In order to make TypeScript happy, I believe you should stick with @TJ Crowder's solution or with this one:为了让 TypeScript 开心,我相信你应该坚持使用 @TJ Crowder 的解决方案或这个:

type Animal = 'dog' | 'cat';

type AnimalSound<T extends Animal> = T extends 'dog'
    ? 'woof'
    : T extends 'cat'
    ? 'meow'
    : never;

const animalSoundMap: { [K in Animal]: AnimalSound<K> } = {
    dog: 'woof',
    cat: 'meow',
};

const lookupSound = <
    AnimalName extends Animal,
    AnimalMap extends { [Name in AnimalName]: AnimalSound<Name> }
>(animalMap: AnimalMap, animal: AnimalName):
    AnimalMap[AnimalName] =>
    animalMap[animal]

If you want to infer return type, you should also infer and make a part of function arguments animalMap .如果您想推断返回类型,您还应该推断并制作函数参数animalMap的一部分。 Playground 操场

You don't even need to define explicit return type, TS is able to infer it from function body:您甚至不需要定义显式返回类型,TS 能够从函数体中推断出它:

const lookupSound = <T extends Animal>(animal: T)=> {
    const sound = animalSoundMap[animal];

    return sound;
}

const result = lookupSound('cat') // "meow"

Conditional types does not work in a way you expect in a place of return type.条件类型在返回类型的地方不能以您期望的方式工作。 It may work if you use conditional type in a function overloading:如果您在函数重载中使用条件类型,它可能会起作用:

function lookupSound<T extends Animal>(animal: T): AnimalSound<T>
function lookupSound<T extends Animal>(animal: T) {
    const sound = animalSoundMap[animal];
    return sound;
}


I think the problem here is caused by AnimalSound<T> being a conditional type, and Typescript resolves conditional types later than other types;我认为这里的问题是由AnimalSound<T>是条件类型引起的,而 Typescript 比其他类型更晚地解析条件类型; specifically, when T extends ... uses a type parameter, it is not resolved until T is bound to a concrete type.具体来说,当T extends ...使用类型参数时,它不会被解析,直到T绑定到具体类型。 So inside the function, where T is just a formal type parameter, it can't reason about AnimalSound<T> in the way you want.所以在函数内部,其中T只是一个形式类型参数,它不能以你想要的方式推理AnimalSound<T>

To avoid this, I recommend making animalSoundMap 's type the one you use:为避免这种情况,我建议将animalSoundMap的类型animalSoundMap您使用的类型:

type AnimalSoundMap = {[K in Animal]: AnimalSound<K>}

const animalSoundMap: AnimalSoundMap = {
    dog: 'woof',
    cat: 'meow',
};

const lookupSound = <T extends Animal>(animal: T): AnimalSoundMap[T] => {
    return animalSoundMap[animal];
}

Playground Link 游乐场链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM