是否可以通过另一个属性值来推断变量属性的类型?

时间:2019-01-28 22:30:57

标签: typescript

我正试图使TypeScript通过其属性值来自动推断变量的类型;

假设我们具有以下功能

function ensureReturnTypeIsRight(...args: any[]): ReturnType {
    return Math.random() > 0.5 ? {
        title: 'prop2',
        params: ['1', '2', '3'],
    } : {
        title: 'prop1',
        params: undefined,
    };
}

type SomeMap = {
    'prop1': string,
    'prop2': (...args: string[]) => string,
}

type ReturnType = `
    Type that supposed to throw an error if title is not a key of SomeMap,
    to throw an error in case title is 'prop2' but no params were provided
    to throw an error in case title is 'prop1' but params were provided
    but not to throw an error in case everything is correct.
    When we try to assign a value to a variable of that type (in our case 'variable' is return value)
`

可以用这样的通用类型完成

type ArgumentTypes<F extends Function> = F extends (...args: infer A) => any ? A : never;
type GenericReturnType<K keyof SomeMap = any> = {
    title: K,
    params: SomeMap[K] extends Function ? ArgumentTypes<SomeMap[K]> : undefined,
};

和写成

的函数
function ensureReturnTypeIsRight(...args: any[]): GenericReturnType {
    const shouldHaveParams = Math.random() > 0.5;

    if (shouldHaveParams) {
        const value: GenericReturnType<'prop2'> = {
            title: 'prop2',
            params: ['1', '2', '3'],
        };

        return value;
    }

    const value: GenericReturnType<'prop1'> = {
        title: 'prop1',
        params: undefined,
    };

    return value;
}

因此TypeScript将检查是否可以将以下类型的值分配给变量。 但是,如果没有泛型,是否可以这样做?

对任何不清楚的想法感到抱歉。

1 个答案:

答案 0 :(得分:2)

您真正要寻找的是工会税,应该像这样:

type FnReturnType = {
    title: "prop1";
    params: undefined;
} | {
    title: "prop2";
    params: string[];
}

这样的联合将确保给定title的返回类型正确。幸运的是,我们可以使用某些映射类型从SomeMap生成这样的联合。

type FnReturnType= {
    [K in keyof SomeMap] -?: { // Transform all properties of SomeMap to a type that is a member of our final union 
        title: K,
        params: SomeMap[K] extends (...args: any[]) => any ? Parameters<SomeMap[K]> : undefined,
    }
}[keyof SomeMap]; // get a union of all types in our previously generated type 

function ensureReturnTypeIsRight(...args: any[]): FnReturnType {
    const shouldHaveParams = Math.random() > 0.5;

    if (shouldHaveParams) {
        return {
            title: 'prop2',
            params: ['1', '2', '3'],
        };
    }

    return {
        title: 'prop1',
        params: undefined,
    };
}