防止TypeScript在通过函数传递字符串文字类型时将其扩展为字符串类型

时间:2018-12-04 23:31:55

标签: typescript

如何防止TypeScript将字符串文字类型扩展为字符串?

例如in the Playground

type PropertyName = 'Property1' | 'Property2';

type ObjectWithPropertyName =
    { Property1: string; } |
    { Property2: string; };

const obj1: ObjectWithPropertyName = {
    ['Property1']: 'works',
}

const prop: PropertyName = 'Property1';
const obj2: ObjectWithPropertyName = {
    [prop]: 'works',
}

const func = (prop: PropertyName) => {
    const obj: ObjectWithPropertyName = {
        [prop]: "fails",
    };
};

const funcToo = (prop: PropertyName) => {
    const propInner: PropertyName = prop;
    const obj: ObjectWithPropertyName = {
        [propInner]: "fails",
    };
};

在最后一种情况下,错误是这样的:

  

类型'{[x:string]:string;类型中缺少属性'Property1'; }”,但在“ ObjectWithPropertyName”类型中为必填项。

2 个答案:

答案 0 :(得分:2)

它适用于const示例的原因是打字稿知道prop只能是'Property1'。当您处理可以具有任意多个值的参数时,typescript会推断出带有索引签名的类型。

例如,虽然不是特别有用,但它也可以工作,因为prop只能是Property1

const func = (prop: 'Property1') => {
    const obj: ObjectWithPropertyName = {
        [prop]: "fails",
    } ;
};

最简单的解决方案是使用类型断言为obj强制使用所需的类型:

const func = (prop: PropertyName) => {
    const obj = {
        [prop]: "fails",
    } as ObjectWithPropertyName;
};

根据您要尝试执行的操作,您可能会考虑采用其他方法,例如,我们可以捕获返回的Record传递的实际常量,其中包含该键是什么:

const func = <T extends PropertyName>(prop: T) => {
    const obj : Record<T, string> = {
        [prop]: "ok",
    };
    return obj;
};
func('Property1').Property1

答案 1 :(得分:2)

它没有通过导致该问题的函数。

“正常”时有效,是因为编译器仍将prop的类型缩小为Property1

const prop: PropertyName = 'Property1';
const obj2: ObjectWithPropertyName = {
    [prop]: 'works',
}

因为如果添加类型断言'Property1' as PropertyName,它会因相同的错误而开始失败

const prop: PropertyName = 'Property1' as PropertyName;
const obj2: ObjectWithPropertyName = {
    [prop]: 'works',
}

基本上,当prop的联合类型为'Property1' | 'Property2'时,值{[prop]: 'something'}不能分配给{Property1: string},也不能分配给{Property2: string},这使得不可分配为联合体类型。

您可以通过进行详尽的检查来使其正常工作:

const func = (prop: PropertyName) => {
    const obj: ObjectWithPropertyName = 
        prop === 'Property1' ? { [prop]: "works" } : { [prop]: "works" }
};