通用类型的 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
}
这可能就是您的 version1
、version2
和 version3
以及 obj4.version = 1
.
工作的原因
好的,希望对您有所帮助。祝你好运!
我想将类型定义为显式属性和泛型类型的混合,在匹配键的情况下显式属性优先。以下是我的尝试,但我在指示的行上收到错误 - 谁能解释为什么或者这是一个 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
}
这可能就是您的 version1
、version2
和 version3
以及 obj4.version = 1
.
好的,希望对您有所帮助。祝你好运!