【问题标题】:Typescript is not able to provide type safety to a simple reducerTypescript 无法为简单的 reducer 提供类型安全
【发布时间】:2021-03-10 18:52:05
【问题描述】:

我有一个简单的reducer,它使用一个对象来累积值。我想尽可能多地使用 TS 推理。 但是我无法让打字稿提供适当的类型安全。它会错误地抱怨,因为空对象不包含任何预期的类型,或者如果我键入对象它根本不会抱怨(而某些键可能会导致未定义的值)。

所以给出这段代码:

type Session = { name: string; startDate: Date; endDate: Date };
type SessionGroup = {
  name: string;
  duration: number;
  sessions: Session[];
};
type Grouped = { [k: string]: SessionGroup };

function selectGroupedSessions(sessions: Session[]): SessionGroup[] {
  const grouped = sessions.reduce((result: Grouped, session) => {
    const { name, startDate, endDate } = session;
    const group = result[name] || { name, sessions: [] };
    group.sessions.push(session);
    result[name] = group;
    return result;
  }, {});
  return Object.values(grouped);
}

如您所见,我将累加器键入为Grouped。如果我这样做,它不会在这一行给我一个错误:result[name] = group 但它应该是因为我分配的默认值与SessionGroup 的预期类型不兼容。问题显然出在Grouped 类型上,它表示,对于每个可能的字符串键,都有一个 SessionGroup,这显然不是真的。

如果我像这样键入 Grouped 类型:

type Grouped = { [k: string]: SessionGroup | undefined };

然后我在最后一行得到以下类型错误(返回 Object.values)

Type '(SessionGroup | undefined)[]' is not assignable to type 'SessionGroup[]'

那么,为了让 typescript 理解它显然是正确的 JS 代码,最好的输入方法是什么?无需添加大量可笑的铸件

Here is a link to a typescript repl

【问题讨论】:

  • 如果您打开checked indexed access,它会打开this。我并不是建议您一定要打开它,但如果您这样做,它会捕获上面代码中的错误。这对你有用吗?如果是这样,我会考虑写一个答案来解释索引签名的这种行为。如果没有,请详细说明发生了什么;你说“它错误地抱怨”,但不包括显示它的minimal reproducible example,所以我有点困惑。
  • 对“错误投诉”的解释在同一行。理想情况下,我不想输入累加器,打字稿应该能够从外部函数签名中推断出来。在我提供的示例中,您所要做的就是删除结果的类型注释,您将看到我所指的错误。激活检查的索引访问似乎产生了预期的效果,但是我不确定它会如何影响代码库的其余部分。我看到的唯一缺点是数组索引,我从不使用,所以应该没问题。
  • 这里有最小可重现的例子:typescriptlang.org/play?#code/…
  • 啊,我明白了。 Contextual typing 不能通过控制流任意向后工作;如果是这样,你在 TypeScript 中几乎不会出错,即使是有明显问题的代码也是如此。如果你写let i = 0; i = "hello",你会得到一个合理的错误,"hello" 不是number。但是有人可能会争辩说,编译器应该使用上下文类型来推断i 应该是string | number,因为您稍后会使用它来存储string。有机会我可以写一个答案。

标签: typescript


【解决方案1】:

不激活任何额外编译器标志(这也可能是一个有效的答案)的一种可能解决方案是使用 Map 而不是对象。 Typescript 似乎明白,即使您为 Map<string, SessionGroup> 类型的 Map 提供类型,get 方法也可能返回预期值或未定义。由于现在大多数现代浏览器都支持地图,这可能是一个有效的解决方案。这是按预期出错的代码:

type Session = { name: string; startDate: Date; endDate: Date };
type SessionGroup = {
  name: string;
  duration: number;
  sessions: Session[];
};

function selectGroupedSessions(sessions: Session[]): SessionGroup[] {
  const grouped = sessions.reduce((result: Map<string, SessionGroup>, session) => {
    const { name, startDate, endDate } = session;
    const group = result.get(name) || { name, sessions: [], };
    group.sessions.push(session);
    result.set(name, group)
    return result;
  }, new Map());
  return [...grouped.values()]
};

typescript playground

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-08
    • 1970-01-01
    • 2021-12-04
    • 1970-01-01
    相关资源
    最近更新 更多