繁体   English   中英

如何从对数组中输入 object map?

[英]How to make typed object map from array of pairs?

假设我有一组这样的对:

const attributes = [
  ['Strength', 'STR'],
  ['Agility', 'AGI'],
  ['Intelligence', 'INT']
  // ..
] as const;

我想生成类似的东西:

const attributesMap = {
  Strength: {
    name: 'Strength',
    shortName: 'STR',
  },
  Agility: {
    name: 'Agility',
    shortName: 'AGI',
  },
  // ..
};

它应该是完全输入的,所以如果我想访问attributesMap.Strength.shortName我会知道它将是"STR"

这个怎么打?

我想出的最好的就是这个,但它并没有按预期工作,因为只有键被输入为 const 值, shortName仍然是所有值的联合,我不知道如何使它成为非联合值:

type mapType = {
  [P in typeof attributes[number][0]]: {
    name: P;
    shortName: typeof attributes[number][1];
  };
};

你快到了:

type mapType = {
  [P in typeof attributes[number][0]]: {
    name: P;
    // Wrap with Extract here
    shortName: Extract<typeof attributes[number], readonly [P, unknown]>[1];
  };
};

结果是

type mapType = {
    Strength: {
        name: "Strength";
        shortName: "STR";
    };
    Agility: {
        name: "Agility";
        shortName: "AGI";
    };
    Intelligence: {
        name: "Intelligence";
        shortName: "INT";
    };
}

@A_Blop 的回答是正确的; 我只是想跟进一个使用 TypeScript 4.1 的密钥重新映射功能作为使用Extract的替代方法的版本。 鉴于:

const attributes = [
    ['Strength', 'STR'],
    ['Agility', 'AGI'],
    ['Intelligence', 'INT']
    // ..
] as const;

让我们给attributes的类型一个我们以后可以使用的名称,为了使重新映射更容易,让我们对其进行限制,使其只有每个元素的数字文字键(即012 ),而不是任何Arraypushpop等键:

type Atts = Omit<typeof attributes, keyof any[]>;
/* type Atts = {
    readonly 0: readonly ["Strength", "STR"];
    readonly 1: readonly ["Agility", "AGI"];
    readonly 2: readonly ["Intelligence", "INT"];
} */

有了Atts ,我们现在可以使用as子句进行重新映射。 对于Atts中的每个键I ,类型Atts[I][0]是键/ name ,类型shortName Atts[I][1]是短名称:

type MappedAttributes = {
    -readonly [I in keyof Atts as Atts[I][0]]: {
        name: Atts[I][0],
        shortName: Atts[I][1]
    }
};

/*
type MappedAttributes = {
    Strength: {
        name: "Strength";
        shortName: "STR";
    };
    Agility: {
        name: "Agility";
        shortName: "AGI";
    };
    Intelligence: {
        name: "Intelligence";
        shortName: "INT";
    };
}
*/

(我也删除了readonly ,但如果你不想这样做,你不必这样做)。 这就是你想要的类型!

Playground 代码链接

您可以使用递归类型:

type MapType<T extends readonly (readonly [string, unknown])[]> = 
    // if the list can be split into a head and a tail
    T extends readonly [readonly [infer A, infer B], ...infer R]
        // these should always be true but typescript doesn't know that
        ? A extends string ? R extends readonly (readonly [string, unknown])[]
            ? {
                // this'll be just one property
                [K in A]: {
                    name: A,
                    shortName: B
                }
            // we intersect with the type recursed on the tail
            } & MapType<R>
            // these should never happen
            : never : never
        // base case, empty object
        : {};

游乐场链接

或者,为了避免重复readonly ,您可以创建一个递归地摆脱它的类型:

type RemoveReadonly<T> = T extends readonly unknown[] ? {
    -readonly [K in keyof T]: RemoveReadonly<T[K]>;
} : T;

type MapType<T extends [string, unknown][]> = 
    // if the list can be split into a head and a tail
    T extends [[infer A, infer B], ...infer R]
        // these should always be true but typescript doesn't know that
        ? A extends string ? R extends [string, unknown][]
            ? {
                // this'll be just one property
                [K in A]: {
                    name: A,
                    shortName: B
                }
            // we intersect with the type recursed on the tail
            } & MapType<R>
            // these should never happen
            : never : never
        // base case, empty object
        : {};

游乐场链接

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM