给出以下示例:
type Dictionary = {
[key: string] : string | undefined
}
function greet(name: string) {
return 'Hello ' + name + '!';
}
function callGreetError(key: string, d: Dictionary) {
if (typeof d[key] !== 'undefined') {
return greet(d[key]) // TS Error: Argument of type 'string | undefined' is not assignable to parameter of type 'string'.Type 'undefined' is not assignable to type 'string'.(2345)
}
return key + ' is not in dictionary';
}
function callGreetNoError(key: string, d: Dictionary) {
const storedVal = d[key];
if (typeof storedVal !== 'undefined') {
return greet(storedVal) // Error goes away when value is stored in an external var.
}
return key + ' is not in dictionary';
}
我试图理解为什么callGreetError
内d[key]
类型的if
不能被推断为string
,即使我明确告诉TS不是undefined
。
为什么将d[key]
的值存储在storedVal
的外部变量callGreetNoError
上可以解决此错误。
答案 0 :(得分:1)
只需创建一个临时变量,这样就不必每次都分别访问该值。对于所有打字稿,都知道该值可能在检查和使用之间发生了变化:
function callGreetError(key: string, d: Dictionary) {
const temp = d[key];
if (temp !== undefined) {
return greet(temp)
}
return key + ' is not in dictionary';
}
在这种情况下,检查typeof temp !== "undefined"
和temp !== undefined
都可以
显示问题的最简单示例是手动将get运算符分配给字典键。在下面的代码示例中,您可以看到key的值确实始终是一个字符串,满足类型要求,但是每次访问它都会改变
const testDict: Dictionary = {};
Object.defineProperty(testDict, "key", {
get() { return Math.random().toString() }
});
console.log(testDict.key);
console.log(testDict.key);
因此与第一次访问检查类型并与第二次访问一起使用是不相关的
答案 1 :(得分:1)
在return语句中的d [key]上添加!可以告诉TypeScript在这种情况下d [key]不会是不确定的,尽管通常是可以的。这就是所谓的non-null assertion operator。
function callGreetError(key: string, d: Dictionary) {
if (typeof d[key] !== 'undefined') {
return greet(d[key]!)
}
return key + ' is not in dictionary';
}
一个新的!后缀表达式运算符可用于断言其 在以下类型的上下文中,操作数是非null和undefined的: 检查者无法得出结论。具体来说,操作 X!产生x类型的值,其中排除了null和undefined。 类似于形式为x和x的类型断言,即T! 非null断言运算符被简单地删除在发出 JavaScript代码。
答案 2 :(得分:1)
基本上,TS不会像key
那样缩小computed property names的属性访问类型。
const d: Dictionary = {...};
const key = "foo";
const fooProp = d[key];
// using .length as string type check
d["foo"] !== undefined && d["foo"].length; // ✔
fooProp !== undefined && fooProp.length; // ✔
d[key] !== undefined && d[key].length; // error, possibly undefined
不是 ,因为TS会进行一些可变性检查,并警告d[key]
值可能在检查和使用之间发生了变化。例如,以下代码对于编译器而言是完美的,但可能会在运行时抛出:
const testDict: Dictionary = {
get foo() { return Math.random() > 0.5 ? "hey" : undefined }
};
function callGreetError(d: Dictionary) {
// compiles fine, but throws error at run-time from time to time
if (d["foo"] !== undefined) d["foo"].length
}
callGreetError(testDict)
要允许使用control flow适当缩小变量,TS必须清楚地知道您的意思是什么:通过使用点表示法d.foo
或使用括号表示法和诸如d["foo"]
的文字进行属性访问。
使用const storedVal = d[key]
的“技巧”起作用,因为TS推断storedVal
的变量类型为string | undefined
。由于控制流分析通常基于变量,因此编译器现在可以更轻松地通过检查storedVal
来缩小undefined
的范围。
答案 3 :(得分:-1)
尝试这样做:
function callGreetError(key: string, d: Dictionary) {
return d?.[key]
? greet(d[key])
: key + ' is not in dictionary';
}
通过这种方式,您可以确保key
不仅是d[key]
都没有定义。如果语法使您感到困惑,则可以阅读有关可选链接here的更多信息。