从另一个对象的值推断出类型键

时间:2020-04-07 13:34:25

标签: javascript typescript type-inference

我有一个界面专栏。

interface column {
  field: string
  label: string
}

所以一排列将是:

const cols = [{
    label: 'First Name',
    field: 'fname',
  },
  {
    label: 'Last Name',
    field: 'lname',
  },
  {
    label: 'Email',
    field: 'email',
  }]

数据将是数据行的数组。

const data:{}[] = [{
fname: 'bob', lname: 'dylan', email: 'db@email.com',
fname: 'van', lname: 'halen', email: 'vh@email.com'
}]

有没有一种方法可以将数据的键(即[fname,lname,email])强制为相应cols [n] ['field']的值? 预先感谢。

3 个答案:

答案 0 :(得分:1)

是的,只要您以这样的方式定义cols,以使编译器不会将field属性扩展为string,就可以这样做。使用TS3.4 +的最简单方法是使用const assertion

const cols = [{
  label: 'First Name',
  field: 'fname',
},
{
  label: 'Last Name',
  field: 'lname',
},
{
  label: 'Email',
  field: 'email',
}] as const;

as const意味着cols将被视为只读元组,其中labelfield属性具有string literal类型。

您可以在此处创建类型别名,该别名将Column兼容的值集转换为以field属性为键的对象类型。当然,没有提到每个字段的值类型是什么,因此我假设它始终为string

interface Column {
  field: string
  label: string
};

type StringObjFromColumns<T extends readonly Column[]> =
  { [K in T[number]["field"]]: string }

然后我们可以将您的数据类型定义为StringObjFromColumns<typeof cols>

const data: StringObjFromColumns<typeof cols>[] = [{
  fname: 'bob', lname: 'dylan', email: 'db@email.com',
}, {
  fname: 'van', lname: 'halen', email: 'vh@email.com'
}, {
  fname: 'van', lname: 'morrison', age: 75, email: 'vm@example.com' // error! age is extra
}, {
  fname: 'ted', lname: 'nugent' // error! email is missing
}]

您可以看到它如何强制每个对象必须具有string类型的这三个字段的约束。

好的,希望能有所帮助;祝你好运!

Playground link to code

答案 1 :(得分:0)

这是不可能的,因为打字稿类型是在编译时而不是运行时强制执行的。请参阅我对this question的回答,它会回答您的问题。

答案 2 :(得分:-1)

这是一个棘手的问题。

interface Column {
    field: string
    label: string
}

我们可以定义一个RowObject,此外,仅出于示例目的,一个Row具有column属性和row本身。

interface RowObject {
    [key: string]: string;
}

interface Row {
    cols: Column[];
    object: RowObject;
}

现在,我们应该定义一个函数来原型化新对象。

const createRowBlueprint = (cols: Column[]): Row => {
    return {
        cols: [...cols],
        object: cols.map(c => c.field)
                    .reduce((prop: RowObject, prev: string) => (prop[prev] = '', prop), {})
    };
};

我刚刚定义了Row接口来动态访问“真实行对象”属性,就像这样

const row = createRowBlueprint(cols);
row.cols.forEach(col => {
    const { field } = col;
    const value = row.object[field];
    console.log(`Field ${field} binded to ${value}`);
});

鉴于您的columns集合,将输出以下内容

{
  cols: [
    { label: 'First Name', field: 'fname' },
    { label: 'Last Name', field: 'lname' },
    { label: 'Email', field: 'email' }
  ],
  row: { fname: '', lname: '', email: '' }
}

Field fname binded to
Field lname binded to
Field email binded to

很明显,我将默认值设置为空字符串。但是,有效的证明是从字段访问的值不是undefined

最后,这种方法不是安全类型。但是,据我所知,Typescript目前暂时不支持动态类型定义。所以这是一个棘手的解决方法。

希望有帮助。