gpt4 book ai didi

typescript - 如何以适当的类型安全方式迭代 Record 键?

转载 作者:行者123 更新时间:2023-12-03 16:55:41 26 4
gpt4 key购买 nike

让我们在一些属性上构建一个示例记录。

type HumanProp = 
| "weight"
| "height"
| "age"

type Human = Record<HumanProp, number>;

const alice: Human = {
age: 31,
height: 176,
weight: 47
};

对于每个属性,我还想添加一个人类可读的标签:
const humanPropLabels: Readonly<Record<HumanProp, string>> = {
weight: "Weight (kg)",
height: "Height (cm)",
age: "Age (full years)"
};

现在,使用这个记录类型和定义的标签,我想迭代两个具有相同键类型的记录。
function describe(human: Human): string {
let lines: string[] = [];
for (const key in human) {
lines.push(`${humanPropLabels[key]}: ${human[key]}`);
}
return lines.join("\n");
}

但是,我收到一个错误:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Readonly<Record<HumanProp, string>>'.
No index signature with a parameter of type 'string' was found on type 'Readonly<Record<HumanProp, string>>'.

如何在 Typescript 中正确实现此功能?

澄清一下,我正在寻找的解决方案,无论它是否会使用 Record类型、普通对象、类型、类、接口(interface)或其他东西,应该具有以下属性:
  • 当我想定义一个新的属性时,我只需要在一个地方做(就像上面的 HumanProp 一样),不要重复自己。
  • 在我定义了一个新属性之后,我应该为这个属性添加一个新值的所有地方,比如当我创建 alice 时, 或 humanPropLabels ,点亮类型错误 在编译时,而不是在运行时错误。
  • 遍历所有属性的代码,例如 describe函数,当我创建一个新属性时应该保持不变。

  • 甚至可以用 Typescript 的类型系统实现类似的东西吗?

    最佳答案

    我认为正确的方法是创建一个不可变的键名数组并为其指定一个窄类型,以便编译器将其识别为包含 string literal types而不仅仅是 string .使用 const assertion 最简单:

    const humanProps = ["weight", "height", "age"] as const;
    // const humanProps: readonly ["weight", "height", "age"]

    然后你可以定义 HumanProp就它而言:
    type HumanProp = typeof humanProps[number];

    你的代码的其余部分应该或多或少地按原样工作,除了当你迭代键时,你应该使用上面的不可变数组而不是 Object.keys() :
    type Human = Record<HumanProp, number>;

    const alice: Human = {
    age: 31,
    height: 176,
    weight: 47
    };

    const humanPropLabels: Readonly<Record<HumanProp, string>> = {
    weight: "Weight (kg)",
    height: "Height (cm)",
    age: "Age (full years)"
    };

    function describe(human: Human): string {
    let lines: string[] = [];
    for (const key of humanProps) { // <-- iterate this way
    lines.push(`${humanPropLabels[key]}: ${human[key]}`);
    }
    return lines.join("\n");
    }

    不使用的原因 Object.keys()是编译器无法验证 Human 类型的对象将只有在 Human 中声明的键. TypeScript 中的对象类型是开放的/可扩展的,而不是封闭的/ exact .这允许接口(interface)扩展和类继承工作:
    interface SuperHero extends Human {
    powers: string[];
    }
    declare const captainStupendous: SuperHero;
    describe(captainStupendous); // works, a SuperHero is a Human

    你不会想要 describe()爆炸是因为你传入了 SuperHero , Human 的特殊类型有一个额外的 powers属性(property)。所以不要使用 Object.keys()正确产生 string[] ,您应该使用已知属性的硬编码列表,以便像 describe() 这样的代码如果它们存在,将忽略任何额外的属性。

    并且,如果您将一个元素添加到 humanProps ,您会在 describe() 时看到所需的错误。将保持不变:
    const humanProps = ["weight", "height", "age", "shoeSize"] as const; // added prop

    const alice: Human = { // error!
    age: 31,
    height: 176,
    weight: 47
    };

    const humanPropLabels: Readonly<Record<HumanProp, string>> = { // error!
    weight: "Weight (kg)",
    height: "Height (cm)",
    age: "Age (full years)"
    };

    function describe(human: Human): string { // okay
    let lines: string[] = [];
    for (const key of humanProps) {
    lines.push(`${humanPropLabels[key]}: ${human[key]}`);
    }
    return lines.join("\n");
    }

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

    Playground link to code

    关于typescript - 如何以适当的类型安全方式迭代 Record 键?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61829651/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com