Typescript 为映射对象指定动态变量的类型脚本

Typescript 为映射对象指定动态变量的类型脚本,typescript,Typescript,我正在处理这个问题(从) 给定一个短语,计算该短语中每个单词的出现次数 例如输入“olly-olly-in-come-free” 我很确定我已经在理论上找到了答案,但是我在类型方面遇到了麻烦。拆分字符串后,我运行一个reduce函数,返回一个Map,如下所示: return string.split(' ').reduce((acc: Map<string, number>, word): Map<string, number> => { if

我正在处理这个问题(从)

给定一个短语,计算该短语中每个单词的出现次数

例如输入“olly-olly-in-come-free”

我很确定我已经在理论上找到了答案,但是我在类型方面遇到了麻烦。拆分字符串后,我运行一个reduce函数,返回一个
Map
,如下所示:

    return string.split(' ').reduce((acc: Map<string, number>, word): Map<string, number> => {
      if (acc.has(word)) {
        console.log("working")
        // I've tried this
        return acc[word] + 1
        // And this
        return acc.set(word, acc.get(word) + 1)
      } else {
        return acc.set(word, 1)
      }
    }, new Map<string, number>())
returnstring.split(“”).reduce((acc:Map,word):Map=>{
如果(附件有(文字)){
控制台日志(“工作”)
//我试过这个
返回acc[word]+1
//还有这个
返回acc.set(word,acc.get(word)+1)
}否则{
返回附件集(字,1)
}
},新映射())
我在调整这个过程中遇到的几个问题是

1)
acc
没有“has”方法(当我键入
acc
as
any
而不是Map时)

2)
acc.get(word)
可能未定义,因此不起作用(我检查了它,但Typescript没有看到?)


当然,无论如何,我都有可能做错事,但是有没有办法让我所做的工作正常呢?

A
Map
在JavaScript中,因此TypeScript不会将其键/值对作为属性直接存储在对象上,所以
acc[word]
除非将
映射
替换为类似
{[k:string]:number | undefined}
的字典对象类型,否则表示法将不起作用。这是一个不错的方法,但出于某种原因,您可能希望使用
贴图而不是普通对象,因此我们将继续使用它


TypeScript不理解
Map
实例的
有(x)
方法返回
true
意味着对
get(x)
的后续调用将不会
未定义。甚至没有好的方法来表示这样的约束。您可以扩充,使
has(x)
方法充当
映射
实例或
x
参数的函数,这可能会根据您所做的工作进行排序,但它非常复杂且非常脆弱

如果我想让您的
返回acc.set(word,acc.get(word)+1)
行保持JavaScript中的状态,我建议在
acc.get(word)
的结果上使用。在这里,您知道检查
acc.has(word)
意味着
acc.get(word)
将是一个
数字
,而不是
数字|未定义
,但编译器不知道这一点。当你确信编译器不知道的东西时,使用。下面是它的外观:

if (acc.has(word)) {
  return acc.set(word, acc.get(word)! + 1); // no error now
}
那太好了。全部完成


然而,类型断言只能隐藏潜在的错误。。。通常,最好使用编译器能够实际验证为类型安全的代码,而不是我们必须断言为安全的代码。那么,我们能做些什么让编译器来帮助我们,而不是反过来呢

我们使用
acc.has(word)
来缩小
acc.get(word)
的结果。但是如果我们只保存
acc.get(word)
的结果并直接检查它会怎么样?然后我们可以完全忘记
has()
方法。比如:

const cnt = acc.get(word);
if (typeof cnt !== 'undefined') {
  return acc.set(word, cnt + 1); // no error here
} else {
  return acc.set(word, 1);
}
我们已经明确检查了
cnt
是否为
未定义的
,正常人理解
cnt
if
语句的
true
子句中的
数字

好了,现在我们都结束了


嗯,但是
typeof cnt==“undefined”
很冗长;我们只需检查
cnt
是否为,它适用于除
0
以外的所有
number
值。由于我们将
未定义的
情况视为计数为
0
,因此这样做没有什么坏处:

const cnt = acc.get(word);
if (cnt) {
  return acc.set(word, cnt + 1);
} else {
  return acc.set(word, 1);
}
等等,一旦我们愿意检查真实性,我们就可以使用来确保
cnt
始终是一个
数字

const cnt = acc.get(word) || 0; // always a number
if (cnt) {
  return acc.set(word, cnt + 1);
} else {
  return acc.set(word, 1);
}
但是现在我们知道,
cnt
的唯一错误值是
0
,因此我们可以始终执行
cnt+1

const cnt = acc.get(word) || 0;
return acc.set(word, cnt + 1);
现在,
cnt
只使用了一次,因此不值得将其作为单独的变量:

return acc.set(word, (acc.get(word) || 0) + 1);
哦,一个只包含一个
return
语句的箭头函数可以更改为使用以下语句的函数:

函数countStuff(string:string){
返回字符串
.拆分(“”)
.减少(
(acc:Map,word):Map=>
acc.set(word)(acc.get(word)| | 0)+1),
新地图()
);
}


好了,现在我们都结束了!至少我是。希望有帮助。祝你好运

谢谢你那样为我把它分解,它真的很有用!
return acc.set(word, (acc.get(word) || 0) + 1);
function countStuff(string: string) {
  return string
    .split(" ")
    .reduce(
      (acc: Map<string, number>, word): Map<string, number> =>
        acc.set(word, (acc.get(word) || 0) + 1),
      new Map<string, number>()
    );
}