如何创建泛型类类型作为另一种类型的子集

时间:2018-06-13 09:30:12

标签: typescript

我正在尝试创建一个泛型类型,它被约束为另一个特定类型的子集。不应该允许添加新属性或覆盖现有属性的类型,但应该允许它仅实现指定类型的子集。这就是我所期望的:

type Foo = { a: string; b: number; }

// Case1: valid
new MyClass<{ a: string }>()

// Case2: invalid (does not exist in both types)
new MyClass<{ b: number }>()

// Case3: invalid (does not exist in both types)
new MyClass<{ c: string }>()

// Case4: invalid (does not exist in both types)
// invalid (wrong type of a)
new MyClass<{ a: number }>()

// Case5: invalid (wrong type of a)
new MyClass<{ a: undefined }>()
new MyClass<{ a: null }>()

我需要的是这样的事情:

// Pseudo code (subsets does not exist):
class MyClass<T subsets Foo> {}

extends Foo做的事情,我不想要的事情:

  1. 允许声明其他属性(case3有效)
  2. 强制执行Foo的所有属性(案例1无效)
  3. extends Partial<Foo>所做的事情,我不想要的事情:

    1. 允许声明其他属性(case3有效)
    2. 所有属性的值均可为undefined
    3. 当前状态

      我创建了一个创建另一种类型子集的类型:

      type Subset<T extends S, S> = Pick<T, keyof S>
      

      但是我无法将它作为类的约束来使用...

1 个答案:

答案 0 :(得分:1)

我们可以创建一个不允许指定任何额外成员的限制,强制要求如果存在任何额外属性,则它们的类型为never。我们可以使用Exclude<keyof T, keyof S>执行此操作,仅获取额外的密钥和Record

type Foo = { a: string; b: number; }

type Subset<T, S> = Pick<S, Extract<keyof T, keyof S>> & Partial<Record<Exclude<keyof T, keyof S>, never>>
class MyClass<T extends Subset<T, Foo>> {
    constructor(public t?:T){
        if(t == null) return;
        // t has no accessible members as far as the compiler is concerned 
        // but we can assign it to Partial<Foo> and access fields like this
        let d: Partial<Foo> = t; 
        d.a // works
    }
}

// Case1: valid
new MyClass<{ a: string }>()

// Case2: invalid (does not exist in both types)
new MyClass<{ b: number }>()

// Case3: invalid (does not exist in both types) 
// This would be invalid under extends Partial<Foo> also
new MyClass<{ c: string }>()

// Case3b: invalid (has extra props)
// This would be valid under extends Partial<Foo> but is not under Partial<Foo> & Record<Exclude<keyof T, keyof Foo>, never>
new MyClass<{  a: string, c: string }>()

// Case4: invalid (does not exist in both types)
// invalid (wrong type of a)
new MyClass<{ a: number }>()

// Case5: invalid (wrong type of a) only with strict null checks !
new MyClass<{ a: undefined }>()
new MyClass<{ a: null }>()

Playground link