我正在创建一个简单的类似模式的类型,以便使用React动态生成表.

这些是我写的类型:

type MySchemaField<T, K extends keyof T> = {
    key: K;
    label: React.ReactNode;
    render?: React.ComponentType<{item: T, value: T[K]}>;
};
type MySchema<T> = {
    fields: MySchemaField<T, keyof T>[]; // <-- here is the problem: I don't want to specify the parameter K here because it depends on the single field
};

这就是我希望使用模式的方式:

type MyModel = {
  name: string;
  slug: string;
  location: {address: string, country: string};
};

const schema: MySchema<MyModel> = {
    fields: [
        {key: 'name', label: 'Name'},
        {key: 'slug', label: 'Link', render: ({value}) => value &&
            <a href={`/my-path/${value}`} target="_blank" rel="noreferrer">{value}</a> || null}, // value should be inferred as string
        {key: 'location', label: 'Address', render: ({value}) => <>{value.country}</>, // value should be {address, country}
    ],
};

然后我有一些接受值T和模式MySchema<T>的React组件.

底层(React)代码工作得很好,但我找不到一种方法来正确地将value作为T[key]类型的字段.有没有一种方法可以实现我想要的,或者我应该对模式进行不同的建模?

推荐答案

你希望MySchema<T>fields属性是keyof T中每KMySchemaField<T, K>union.也就是说,你想在K个unions 中创建distribute MySchemaField<T, K>个unions .

有不同的方法可以做到这一点.我在这里的方法可能是制作一个distributive object type(如microsoft/TypeScript#47F<K>年创造的),在这里你可以制作一个mapped type,然后立即制作index into it.像{[K in KS]: F<K>}[KS]这样的东西最终会变成一个F<K>K的联盟.在你的例子中,KSkeyof TF<K>MySchemaField<T, K>.这样地:

type MySchema<T> = {
    fields: Array<{ [K in keyof T]-?: MySchemaField<T, K> }[keyof T]>;
};

让我们看看它是如何工作的:

type MySchemaMyModel = MySchema<MyModel>
/* type MySchemaMyModel = {
    fields: (
      MySchemaField<MyModel, "name"> | 
      MySchemaField<MyModel, "slug"> | 
      MySchemaField<MyModel, "location">
    )[];
} */

这就是你想要的,对吧?现在一切都按照你的意愿推断:

const schema: MySchema<MyModel> = {
    fields: [
        { key: 'name', label: 'Name' },
        {
            key: 'slug', label: 'Link', render: ({ value }) => value &&
            <a href={`/my-path/${value}`} target="_blank" rel="noreferrer">{value}</a> 
            || null
        },
        { key: 'location', label: 'Address', render: ({ value }) => 
          <>{value.country}</>, }
    ],
};

Playground link to code

Typescript相关问答推荐

如何声明循环数组类型?

PrimeNG Inputnumber,具有自定义号码格式器

为什么空对象可以将类型断言为任何内容?

脉轮ui打字脚本强制执行图标按钮的咏叹调标签-如何关闭

使用FormArray在Angular中添加排序

ReturnType此索引方法与点表示法之间的差异

如何判断输入是否是TypeScript中的品牌类型?

使Typescribe强制所有对象具有相同的 struct ,从两个选项

如何使默认导出与CommonJS兼容

在分配给类型的只读变量中维护const的类型

按函数名的类型安全类型脚本方法包装帮助器

自定义 Select 组件的类型问题:使用带逗号的泛型类型<;T与不带逗号的<;T&>;时出错

TypeScrip:使用Union ToInterval辅助对象,但保留子表达式

创建一个TypeScrip对象并基于来自输入对象的约束定义其类型

错误TS7030:并非所有代码路径都返回值.";由tsfig.json中的";Strong";:False引起

必需的输入()参数仍然发出&没有初始值设定项&q;错误

如何在Angular /字体中访问API响应的子元素?

为什么受歧视的unions 在一种情况下运作良好,但在另一种非常类似的情况下却不起作用?

在tabel管理区域react js上设置特定顺序

组件使用forwardRef类型脚本时传递props