typescript 必填&lt; Partial < Inner>&gt;不扩展Inner

jaql4c8m  于 2023-05-30  发布在  TypeScript
关注(0)|答案(1)|浏览(119)

我有一个类似于下面的代码:

type Inner = {
  a: string
}
type Foo<I extends Inner> = { f: I }
interface Bar<I extends Inner> {
  b: I
}
type O<I extends Partial<Inner>> = Foo<Required<I>> & Bar<Required<I>

Playground链接
但是上面的代码无法编译,并出现以下错误:

Type 'Required<I>' does not satisfy the constraint 'Inner'.
  Types of property 'a' are incompatible.
    Type 'I["a"]' is not assignable to type 'string'.
      Type 'string | undefined' is not assignable to type 'string'.
        Type 'undefined' is not assignable to type 'string'.ts(2344)

我期望Required<Partial<Inner>>扩展Inner

thtygnil

thtygnil1#

问题是I extends Partial<Inner>意味着I可以是Partial<Inner>的 * 子类型 *,并且存在Partial<Inner>的子类型-即使通过Required-也与Foo上的I extends Inner约束不兼容。下面是一个此类类型的示例:

type Incompatible = Partial<Inner> & {a: undefined};

这是Partial<Inner>的有效子类型,因为Partial<Inner>的定义是{a?: string | undefined}。但是Required<Incompatible>并没有把它变回Inner的扩展,它只是把它变成了{a: undefined}。如果你尝试像这样对Foo使用它,它会失败,原因和你的Map类型失败的原因一样:

type Q = Foo<Required<Incompatible>>;

Playground链接
如果可以的话,以类型安全的方式修复它,这将非常具体地针对您的真实的情况。例如,它很容易做到:

type RequiredInner<T extends Partial<Inner>> = {
    [Key in keyof T]: T[Key] extends undefined ? Key extends keyof Inner ? Inner[Key] : never : T[Key];
};

...然后使用Foo<RequiredInner<I>>。虽然这是可行的,但它做出了一个主要的假设,这个假设可能不合理:你没有{a: undefined}的运行时值,而你有Partial<Inner>。这个假设不是类型安全的。(但是jcalz在关于这个问题的评论中指出,使用exactOptionalPropertyTypes选项可能会阻止这种赋值,尽管这个标志并不能解决你的问题。)所以我认为我们不能给予你一个通用的方法来在你的真实的代码中解决这个问题。但希望理解这个问题能帮助你解决它。

相关问题