TypeScript 通用函数不接受具有联合和交集的类型参数,

z0qdvdin  于 6个月前  发布在  TypeScript
关注(0)|答案(4)|浏览(64)

Bug报告

🔎 搜索词

generic function union intersection type optional property

🕗 版本与回归信息

  • 在版本3.6.0-dev.20190723和版本3.6.0-dev.20190724之间发生了变化

⏯ Playground链接

带有相关代码的Playground链接

💻 代码

type A = ({ a: any } | { b: any }) & { c?: any };

declare function testA<Param extends A>(param: Param & A): Param;

declare const s1: A;

const a1 = testA(s1);
/**
* Argument of type 'A' is not assignable to parameter of type '{ b: any; } & A'.
*   Type '{ a: any; } & { c?: any; }' is not assignable to type '{ b: any; } & A'.
*     Type '{ a: any; } & { c?: any; }' is not assignable to type '{ b: any; } & { a: any; } & { c?: any; }'.
*       Property 'b' is missing in type '{ a: any; } & { c?: any; }' but required in type '{ b: any; }'.(2345)
*/

type B = ({ a: any } | { b: any }) & { c: any };

declare const s2: B;

const a2 = testA(s2); // Type of result is A but must be B

🙁 实际行为

const a1 = testA(s1);出现错误:

Argument of type 'A' is not assignable to parameter of type '{ b: any; } & A'.
  Type '{ a: any; } & { c?: any; }' is not assignable to type '{ b: any; } & A'.
    Type '{ a: any; } & { c?: any; }' is not assignable to type '{ b: any; } & { a: any; } & { c?: any; }'.
      Property 'b' is missing in type '{ a: any; } & { c?: any; }' but required in type '{ b: any; }'.(2345)

在行const a2 = testA(s2);中,TypeScript编译器推断常量a a2的类型为A

🙂 预期行为

变量s1具有类型A,这意味着function testA<Param extends A>(param: Param & A): Param接受s1。参数类型Param示例化为A
常量a2具有类型B,因为参数s2具有类型B
这是正常的行为,直到version 3.6.0-dev.20190723。从那时起,它导致了错误。

k97glaaz

k97glaaz1#

另外,值得注意的是,当编译器在错误信息中将 { b: any } & A 显示为 { b: any; } & { a: any; } & { c?: any; } 时,它得出了错误的结论。

lstz6jyr

lstz6jyr2#

declare function testA<Param extends A>(param: Param & A): Param;

declare function testA<Param extends A>(param: Param): Param;

有什么意图?

cgfeq70w

cgfeq70w3#

这是一个简化了实际代码中暴露bug的例子。这种模式在类型没有正确缩小的设计限制的通用函数中很有用:

type Animal = { type: 'dog'; forelegs: number } | { type: 'duck'; wings: number };

function calculateForelimbs<A extends Animal>(animal: A): A & { forelimbs: number } {
    return { ...animal, forelimbs: animal.type === 'dog' ? animal.forelegs : animal.wings };
    // won't work due to design limitation
}

可以通过向 animal 参数的类型添加 & Animal 来修复它:

function calculateForelimbsWorkable<A extends Animal>(animal: A & Animal): A & { forelimbs: number } {
    return { ...animal, forelimbs: animal.type === 'dog' ? animal.forelegs : animal.wings };
    // working like a charm
}

function createSuperAnimal(): Animal {
    const legsOrWings = 100500;
    const typeOfAnimal = Math.random() < 0.5 ? 'dog' : 'duck';

    return typeOfAnimal === 'dog'
        ? { type: typeOfAnimal, forelegs: legsOrWings }
        : { type: typeOfAnimal, wings: legsOrWings }
}

const superAnimalWithLimbsCalculated = calculateForelimbsWorkable(createSuperAnimal());

链接到Playground
如果我们使 Animal 类型更复杂:

type Animal = ({ type: 'dog'; forelegs: number } | { type: 'duck'; wings: number }) & { domesticated: boolean };

它仍然有效(链接到Playground)。
当我们决定让属性 domesticated 是可选的时候,结果发现代码是错误的:

type Animal = ({ type: 'dog'; forelegs: number } | { type: 'duck'; wings: number }) & { domesticated?: boolean };

链接到Playground
现在,当版本4.3.2带有改进的泛型上下文缩小功能已经发布时,它变得不那么实际了。现在我们可以跳过 & Animal 部分。
但是这个bug是一个bug。它可能会影响遗留代码,或者是更大问题的一个破损部分。

avkwfej4

avkwfej44#

我找到了这个模式仍然有用的用例:

type A = ({ a: any } | { b: any }) & { c?: any };

declare function f<X extends A>(x: X): X

function testA1<Param extends A | undefined>(param: Param): Param {
    return param && f(param)
    // won't work even in typescript 4.3
};

function testA2<Param extends A | undefined>(param: Param & A): Param {
    return param && f(param)
    // working fine
};

declare const s1: A;

const a1 = testA2(s1); // still error

type B = ({ a: any } | { b: any }) & { c: any };

declare const s2: B;

const a2 = testA2(s2); // wrong type A | undefined, must be B

const a3 = testA1(s1); // A, but testA1 would not work on its own

const a4 = testA1(s2); // B, but testA1 would not work on its own

链接到Playground
在TypeScript 3.3.3333中运行良好:链接

相关问题