如何强制 class 实现可以存储未定义值的属性

How to force class to implement properties that can store undefined value

我正在尝试创建一个映射类型,它会强制 class 实现一组基于某些对象类型的属性。基本上当扩展对象类型时,class也需要扩展。

type SomeObjectType = {
    a?: {/* ... */},
    b?: {/* ... */},
    c?: {/* ... */}
}

type SomeMappedType = {
    [K in keyof SomeObjectType]: SomeGenericClass<K>
}

class SomeClass implements SomeMappedType {
  a?: SomeGenericClass<'a'>;
  b?: SomeGenericClass<'b'>;
  c?: SomeGenericClass<'c'>;
}

上述代码的问题在于,由于 SomeObjectType 中的所有对象属性都是可选的 ? 它不会强制 class 实现它们。

我尝试使用 | undefined 但它也不起作用。让它工作的唯一方法是使用 -?:

摆脱 ?
type SomeMappedType = {
    [K in keyof SomeObjectType]-?: SomeGenericClass<K>
}

class SomeClass implements SomeMappedType {
  a: SomeGenericClass<'a'>;
  b: SomeGenericClass<'b'>;
  c: SomeGenericClass<'c'>;
}

但是我无法将 undefined 值存储到这些属性。

Non-optional 属性(没有?)必须定义,但通过类型联合我们可以指定它们是指定类型还是未定义。

interface MandatoryFoo {
  bar: string | undefined
  baz: string | undefined
}

const x: MandatoryFoo = { bar: undefined, baz: undefined }

可选属性(带有?)可以省略。

interface OptionalFoo {
  bar?: string | undefined
  baz?: string | undefined
}

const y: OptionalFoo = { bar: undefined }

可选属性本质上允许未定义,所以我们实际上不需要联合类型。您可以将 属性 指定为未定义或省略它。

interface OptionalShorthandFoo {
  bar?: string
  baz?: string
}

const z: OptionalShorthandFoo = { bar: undefined }

Playground Link

这是你需要的吗?我使用 Array 进行演示。

type SomeObjectType = {
    a: {/* ... */},
    b?: {/* ... */},
    c?: {/* ... */}
}

type RequiredLiteralKeys<T> = keyof { [K in keyof T as string extends K ? never : number extends K ? never :
    {} extends Pick<T, K> ? never : K]: 0 }

type OptionalLiteralKeys<T> = keyof { [K in keyof T as string extends K ? never : number extends K ? never :
    {} extends Pick<T, K> ? K : never]: 0 }

type SomeMappedType = {
  [K in RequiredLiteralKeys<SomeObjectType>]: Array<K>
} & {
  [K in OptionalLiteralKeys<SomeObjectType>]-?: Array<K> | undefined
}

class SomeClass implements SomeMappedType {
  a!: Array<'a'>;
  b!: Array<'b'>;
  c!: undefined // will error if c is missing but undefined still works
}

所有必填属性保持为必填,所有可选属性将转换为 Array<T> | undefined

Playground