通用类型的 TypeScript 部分

TypeScript partial of a generic type

我想将类型定义为显式属性和泛型类型的混合,在匹配键的情况下显式属性优先。以下是我的尝试,但我在指示的行上收到错误 - 谁能解释为什么或者这是一个 tsc/compiler 错误?

// Takes properties in A and B. For matching properties, the types in A are used.
type Mix<A, B> = {
  [K in keyof B | keyof A]: K extends keyof A
    ? A[K]
    : K extends keyof B
    ? B[K]
    : never
}

type Versionable = { version: number }

function test<T>(): void {
  const version1: Partial<Mix<Versionable, T>>['version'] = 1 // compiles - version type is correctly inferred as number | undefined
  const version2: Partial<Mix<Versionable, T>>['version'] = undefined // compiles
  const version3: Partial<Mix<Versionable, T>>['version'] = '1' // does not compile as expected

  const obj1: Partial<Mix<Versionable, T>> = { version: 1 } // DOES NOT COMPILE.... WHY??
  const obj2: Partial<Mix<Versionable, T>> = { version: undefined } // compiles
  const obj3: Partial<Mix<Versionable, T>> = { version: '1' } // does not compile as expected
  const obj4: Partial<Mix<Versionable, T>> = {} // compiles
  obj4.version = 1 // compiles
}

我认为这里的行为与 microsoft/TypeScript#13442 中的相同;几乎没有具体的值被编译器视为可分配给 Partial<SomethingDependingOnAGenericTypeParam>,除了具有 undefined 类型属性的值(这对 obj2 的行为负责)或空对象(这是对 obj4 的行为负责。

这个限制通常是合理的,因为这些赋值通常是不安全的:

function unsafeCaught<T extends { a: string }>() {
  const nope: Partial<T> = { a: "" }; // CORRECT error  
}
interface Oops { a: "someStringLiteralType" };
unsafeCaught<Oops>(); // {a: ""} is not a valid Partial<Oops>

(这是造成 obj3 行为的原因。)但该限制也阻止了某些已知安全的,比如你的,这是专门制作的:

function safeCaught<T extends { a: string }>() {
  const stillNope: { [K in keyof T]?: K extends "a" ? string : T[K] }
    = { a: "" }; // incorrect error
}

(这是 obj1 行为的原因。)在这些情况下,编译器根本没有执行使其工作所需的分析。 similar issue, microsoft/TypeScript#31070 was actually considered to be a bug and fixed, but in that case the mapped property type was a constant like number whereas in yours it's a conditional type. And the compiler already does a rather poor job of verifying assignability to conditional types that depend on unresolved generic parameters. So for your case I'd chalk this up to a design limitation of TypeScript and use a type assertion 表明在这种情况下您比编译器更了解:

const obj1 = { version: 1 } as Partial<Mix<Versionable, T>>; // okay now

奇怪的是,当您写入 属性 而不是将对象分配给变量时,此限制会放宽,因此您最终会得到完全相同的不合理行为且没有错误:

function unsafeUncaught<T extends { a: string }>(val: Partial<T>) {
  val.a = ""; // uh, wait
}
const oops: Oops = { a: "someStringLiteralType" };
unsafeUncaught(oops);

我是 not sure why 但这可能是某处的设计决定。因此,以下内容也不会给您带来任何错误,但只是巧合,因为它首先没有正确检查:

function safeUncaught<T extends { a: string }>(
  val: { [K in keyof T]?: K extends "a" ? string : T[K] }
) {
  val.a = ""; // okay but coincidentally
}

这可能就是您的 version1version2version3 以及 obj4.version = 1.

工作的原因

好的,希望对您有所帮助。祝你好运!

Link to code