繁体   English   中英

TypeScript联合类型推断失败

[英]TypeScript union type inference failure

似乎有时TypeScript(最新版本)无法缩小联合类型,即使存在类型保护。 此行为是错误还是功能:

前言:

// some config
interface Config {
    name: string;
    option1?: number;
    option2?: boolean;
}

// arbitrary type
interface Entity {
    a: number;
    b: number;
}

// name aware type guard for entity property-to-config map
// some default config may be replaced with a property name
type TConfigSet<TData> = {
    [P in keyof TData]: (Config & { name: P }) | P;
}

// example of TConfigSet usage
const EntityConfigs: TConfigSet<Entity> = {
    a: {
        name: 'a',
        option2: true
    },
    b: 'b'
}

题:

// this function compiles
function TypeLooseFieldToName(name: string | Config): string {
    if (typeof name === 'string') return name;
    else return name.name;
}

// this one doesn't
function TypeStrictFieldToName<TData>(name: keyof TData | { name: keyof TData }): keyof TData {
    if (typeof name === 'string') return name;
    else return name.name; // still keyof TData | { name: keyof TData }, should be shrinked to { name: keyof TData }
}

它似乎是类型检查器中的一个错误,因为TypeScript手册说“ keyof T类型的键被认为是string的子类型。”

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html

作为解决方法,可以反转类型保护以首先排除自定义类型:

function hasName(obj: string | { name: string }): obj is { name: string } {
    return typeof obj.name === 'string';
}

function getName<TData>(name: keyof TData | { name: keyof TData }): keyof TData {
    if (hasName(name)) return name.name;
    else return name;
}

// compiles with valid keys
getName<Entity>('a');
getName<Entity>({ name: 'a' });

// doesn't compile with invalid keys
getName<Entity>('z');
getName<Entity>({ name: 'z' });

您可以在GitHub中搜索TypeScript问题并提交新问题(如果以前未解决过这个问题):

https://github.com/Microsoft/TypeScript/issues

typeof name === 'string'不能作为keyof TData类型保护,因为编译器假定stringkeyof TData是两种不同的类型。 如果您为keyof TData添加自己的自定义类型保护,它可以工作:

function TypeStrictFieldToName<TData>(name: keyof TData | { name: keyof TData }): keyof TData {
    if (isKeyofTData<TData>(name)) return name;
    else return name.name; // type of name here is { name: keyof TData }
}

function isKeyofTData<TData>(name: keyof TData | {}): name is keyof TData {
    return typeof name === 'string';
}

暂无
暂无

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

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