【问题标题】:How to infer index signatures correctly?如何正确推断索引签名?
【发布时间】:2019-08-12 20:31:55
【问题描述】:

似乎将输入 arg 推断为通用索引签名并没有按预期工作(或者我完全遗漏了什么????)。

如何推断返回类型并正确验证输入?

interface Styles {
  contentAlign?: string;
  zIndex?: number;
}

function createTheme<S extends { [key: string]: Styles }>(theme: S) {
  return theme;
}

// this works, foo is marked as invalid
const style: Styles = {
  zIndex: 1,
  foo: 'bar', // <-- invalid
};

// once I try to use the Styles as index signature it allows other properties
const t = createTheme({
  Button: {
    zIndex: 1,
    foo: 'bar', // <-- valid??
  },
});

我期待Type { foo: "bar" } is not assignable to type Styles,但它似乎是一个有效的输入

【问题讨论】:

  • 我想完成那个变量t 具有传入的对象的推断类型:js { Button: { zIndex: number } }

标签: typescript typescript-generics


【解决方案1】:

S extends { [key: string]: Styles } 意味着S 可以是{ [key: string]: Styles } 的子类型。但这也意味着S 的任何属性也可以是Styles 的子类型,因此这意味着任何给定的键实际上可以具有比Styles 中指定的属性更多的属性。

通常在 OOP 中,允许在需要基类型的地方分配子类型,Typescript 仅在将对象字面量直接分配给特定类型时才执行多余的属性检查。分配给泛型类型参数时,编译器不会执行过多的属性检查,因为它假定您希望允许子类型(毕竟 S extends {...} 读取任何扩展 {...} 的类型 S)。

在您的情况下,因为您希望允许任何键,但您实际上并不想禁用 Styles 上的多余属性检查,我将使用对象的键而不是整个对象作为类型参数:

interface Styles {
  contentAlign?: string;
  zIndex?: number;
}

function createTheme<K extends PropertyKey>(theme: Record<K, Styles>) {
  return theme;
}

// this works, foo is marked as invalid
const style: Styles = {
  zIndex: 1,
  foo: 'bar', // <-- invalid
};

// once I try to use the Styles as index signature it allows other properties
const t = createTheme({
  Button: {
    zIndex: 1,
    foo: 'bar', // <-- error
  },
  Header: {
    zIndex: 1,
    foo: 'bar', // <-- error
  },
});

Play

【讨论】:

  • 非常感谢!这按预期工作?,很高兴我学到了另一个有用的东西
猜你喜欢
  • 1970-01-01
  • 2019-08-29
  • 1970-01-01
  • 1970-01-01
  • 2022-06-10
  • 2016-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多