网站搜索

reactjs - 确保类型 A 扩展具有字段且该字段是另一个对象的值的类型 B


我想确保类型 A 有一个通用类型的字段,但该字段的名称由另一个对象的某个字段的值定义。

例如,该函数:

interface IndexedContext<K extends string, T> {
  context: T;
  key: K;
}

type TypeB<P, K, T> = {
  // this field should be named after IndexedContext[key]
  P[K] = T;
}

const Consumer =
  <T extends object, K>(context: IndexedContext<K, T>) => <C extends ComponentType<TypeA>, TypeA extends TypeB: C => {
    .....
  };

TypeA(props)应该有一个字段,它的值是 IndexedKey 中的字段 key?这样当我在一个 React 组件上使用这个装饰器并传递一个 ReactContext 给它时,我就可以确保 props 有一个与 key 相同的字段。

@Consumer({context: MyContext, key: 'myKey'})
class MyClass extends Component<MyProps> {}


interface MyProps {
  // compiler should fail if this key is missing.
  myKey: // Type of value of MyContext
}

答案#1

你不需要定义任何额外的映射类型(例如在你的示例中应该有的 TypeB),你可以使用 Record 从字符串字面量和字段类型获取映射类型。

你还需要获取实例类型,而不是构造函数。如果你写 {context: MyContext, key: 'myKey'},context 将是类 MyContext,因此 T 会被推断为 typeof MyContext 而不是 MyContext。要获取实例类型,你可以将 context 类型定义为 new (...a:any[]) => T。

将所有部分结合起来:

interface IndexedContext<K extends string, T> {
    context: Type<T>;
    key: K;
}
type Type<T> = new (...a: any[]) => T

const Consumer =
    <T extends object, K extends string>(context: IndexedContext<K, T>) => <C extends ComponentType<Record<K, T>>>(cls: C) => {

    };

class MyContext { }
@Consumer({ context: MyContext, key: 'myKey' })
class MyClass extends Component<MyProps> { }


interface MyProps {
    // compiler WILL fail if this key is missing.
    myKey: MyContext// Type of value of MyContext
}

注意 context 必须被分配一个类才能生效,你将无法直接使用接口或基本类型。