【问题标题】:Return generic on a function without knowing for the specific parameters of it (the generic)在不知道函数的具体参数的情况下返回函数的泛型(泛型)
【发布时间】:2020-09-18 04:04:49
【问题描述】:

我被困在一个函数的类型定义上,该函数接受包含鉴别器的泛型。

我需要的是确保函数始终返回MyType,这与泛型内部类型的细节无关,但我希望这是“推断”的。例如。我不必写我要返回的特定MyType,因为它将“合并”(甚至替换)返回值

例如,这里我收到一个包含 3 个 MyType 相关类型的类型,但只返回 2 个。


interface MyType <Tag, Type> {
    tag: Tag;
    value: Type;
}

type SampleInputType = MyType<'a number', number> | MyType<'a string', string> | MyType<'foo', boolean>;

const sampleInputValue: SampleInputType = {
    tag: 'a number',
    value: 10
};

// Ideally would like to accept any MyType<W, X> and return any MyType<Y, Z>
// But not MyType<any, any>
// Would be fine with only the return type
const myFunction = (input) => {
    if (input.tag === 'a number') {
        return {
            tag: 'foo',
            value: true
        };
    } else {
        return {
            // Sample of type mistaked i would like to avoid, as this convert the return value to any silently
            tagx: 'a number', // This should be a compiler error
            value: 10
        }
    }
};


// Sample output type
type SampleOutputType = MyType<'a number', number> | MyType<'foo', boolean>;

// myVal only posibilities should only be MyType<'a number', number> and MyType<'foo', boolean>
const myVal = myFunction(sampleInputValue);

if (myVal.tag === 'foo') {
    const b: boolean = myVal.value; // this is any :/
}

【问题讨论】:

    标签: typescript typescript-generics


    【解决方案1】:

    首先,如果判别器的值始终为string literal type,那么您可能应该将constrain 对应的参数设置为string,以便编译器推断这一点并捕获更多错误:

    interface MyType<T extends string, V> {
        tag: T;
        value: V;
    }
    

    告诉编译器您正在返回一个有效的MyType&lt;T, V&gt; 而无需实际指定泛型的最简单方法可能是使用这样的辅助函数:

    const myType = <T extends string, U>(x: MyType<T, U>) => x;
    

    这只是运行时的标识函数(对于所有xmyType(x) 只是x),但它会强制执行您关心的约束。然后,在您的myFunction 实现中,通过myType() 传递您的返回值:

    const myFunction = (input: MyType<string, unknown>)=> {
        if (input.tag === 'a number') {
            return myType({
                tag: 'foo',
                value: true
            });
        } else {
            return myType({
                tagx: 'a number', // error!
            //  ~~~~~~~~~~~~~~~~
            //  Object literal may only specify known properties, 
            //  but 'tagx' does not exist in type 'MyType<string, unknown>'. 
            //  Did you mean to write 'tag'?
                value: 10
            });
        }
    };
    

    注意输入类型是MyType&lt;string, unknown&gt;,我必须把它写出来。我认为这没关系,正如您在问题中提到的那样;如果不是,我所能想象的只是给MyType 的泛型参数一些defaults,这样你就可以写MyType 来表示MyType&lt;string, unknown&gt;。不过,继续前进:

    嘿,tagx 出现了您想要的错误。修复它后,编译器会将 myFunction() 视为以下类型:

    const myFunction: (input: MyType<string, unknown>) => 
      MyType<"foo", boolean> | MyType<"a number", number>
    

    所以它推断返回是一个有区别的联合,这意味着你可以做你现在想要的那种控制流分析测试:

    if (myVal.tag === 'foo') {
        const b = myVal.value; // b is boolean
    } else {
        myVal.value.toFixed(2); // myVal.value is number
    }
    

    更新:如果您希望“我希望函数返回一些 MyType&lt;?,?&gt;”的规范发生在函数级别而不是返回的值中,您可以使用不同的辅助函数,像这样:

    const myTypeReturner = <I extends any[], M extends MyType<string, unknown>>(
        x: (...args: I) => M) => x;
    

    然后将您的整个 myFunction 实现传递给它:

    const myFunction2 = myTypeReturner(
        (input: MyType<string, unknown>) => { // error!
    //  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //  Type '{ tagx: string; value: number; tag?: undefined; }' 
    //  is not assignable to type 'MyType<string, unknown>'
            if (input.tag === 'a number') {
                return {
                    tag: 'foo',
                    value: true
                };
            } else {
                return {
                    tagx: 'a number',
                    value: 10
                };
            }
        });
    

    这也会给您一个错误......但不幸的是,它与您认为的“问题​​”线相去甚远。编译器说“我不喜欢这个函数”,而不是“我不喜欢函数中的特定行”。所以这就是为什么我更喜欢我上面概述的方法。但无论哪种方式都有效。

    Playground link

    【讨论】:

    • 谢谢。我的问题不够具体,但我想在功能级别执行此操作,例如const myFunction: (something) =&gt; OneOf MyTypeconst myFunction = () : OneOfMyType ..。我想这还不可能?其他问题是类似的适用于输入值,方式设置MyType&lt;string, unknown&gt;使得“标签”不再是“数字” | “富” |函数内部的“a string”,但“string”。
    • 对于输入,我不知道您希望编译器如何在不自己注释的情况下知道它。比如你可以写(input: SampleInputType) =&gt; ...,然后编译器就会知道tag就是"a number" | "foo" | "a string"。我的意思是,这不是真的可以推断的,是吗?看函数实现我当然不能推断出来。
    • 更新了另一种方法的答案,使其更接近“功能级别”。不,如果不指定泛型类型,任何类型的OneOf,不直接支持existential typesgeneric values,就无法注释泛型类型。根据我的经验,辅助函数是你能到达那里的最接近的函数。
    • For the input I have no real idea how you expect the compiler to know it without annotating it yourself 对不起,你是对的。我已经有了类型,我只是想确保它是 MyType&lt;string, something&gt; 类型,但这并不重要。
    猜你喜欢
    • 2012-09-19
    • 1970-01-01
    • 2022-01-04
    • 2020-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-18
    • 1970-01-01
    相关资源
    最近更新 更多