我正在解决这个问题(来自Exercism)
给出一个短语,计算该短语中每个单词的出现次数。
例如,输入“ olly olly in free”
olly: 2
in: 1
come: 1
free: 1
我很确定我在理论上已经找到了答案,但是我在类型方面遇到了麻烦。分割字符串后,我运行一个reduce函数,返回一个Map<string, number>
,如下所示:
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>())
在进行调整时遇到了一些问题
1)acc
没有“具有”方法(当我将acc
键入any
而不是Map时)
2)acc.get(word)
可能是未定义的,因此不起作用(我检查了一下,但是Typescript没有看到?)
当然,无论如何我都会犯错,但有什么办法可以使我的工作取得成功吗?
答案 0 :(得分:0)
JavaScript中的Map
,因此TypeScript不会将其键/值对作为属性直接存储在对象上,因此,除非将acc[word]
替换为Map
,否则{[k: string]: number | undefined}
表示法将不起作用。像Map
这样的字典对象类型。这并不是一个坏方法,但是由于某种原因,您大概想使用Map
而不是普通对象,所以我们会坚持下去。
TypeScript无法理解has(x)
实例的true
方法返回get(x)
的意思是对undefined
的后续调用不会是has(x)
。代表这样的约束也没有好办法。您可以扩展existing typings for Map
,以使Map
实例或x
参数的return acc.set(word, acc.get(word) + 1)
方法充当type guard function,这可能工作,具体取决于您在做什么,但这非常复杂,而且非常脆弱。
如果我想在JavaScript中将acc.get(word)
行保持原样,我想在acc.has(word)
的结果上使用non-null assertion operator !
。在这里,您知道检查acc.get(word)
意味着number
将是number | undefined
而不是if (acc.has(word)) {
return acc.set(word, acc.get(word)! + 1); // no error now
}
,但是编译器并不知道这一点。而且,只要您确定编译器不知道某些内容,就可以使用type assertion。这是它的外观:
acc.has(word)
那太好了。全部完成!
不过,类型断言仍然可以隐藏潜在的错误……通常更可取的是,使用编译器实际上可以将其验证作为类型安全的代码,而不是我们必须进行断言的代码 em>是安全的。那么,如何使编译器帮助我们而不是反过来呢?
好吧,我们使用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
我们已明确检查undefined
是否为cnt
,普通control flow analysis知道number
是{{1}内的true
if
语句的}子句。
好的,现在我们都完成了!
嗯,但是typeof cnt === "undefined"
是罗word的;我们只需要检查cnt
是否为truthy,是否适用于number
以外的所有0
值即可。而且由于无论如何我们都将undefined
的情况视为0
,因此这样做并没有什么害处:
const cnt = acc.get(word);
if (cnt) {
return acc.set(word, cnt + 1);
} else {
return acc.set(word, 1);
}
等等,一旦我们愿意检查真实性,就可以使用logical "or" operator来确保cnt
总是 一个number
:< / p>
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
语句的箭头功能可以更改为使用concise body的箭头功能:
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>()
);
}
好的,现在我们都完成了!或至少是我。希望能有所帮助。祝好运!