因此,假设您正在使用生产代码库进行工作,该生产代码库是用打字稿完成的,但是它很旧并且有很多隐式any
-类似object[key]
之类的东西。您可能决定关闭noImplicitAny
标志,以免出现大量的编译错误。
但是,当您向项目中添加新代码时,希望在使用方括号表示法时有更好的安全性。我认为您应该可以简单地使用Type Assertion来执行此操作,但是我看到的是它仍然隐式地将文字推断为any
(除非字符串靠近键,更多内容在下面...) 。对我来说,这似乎是个错误,但如果有人能在我打开问题之前先解释一下我做错了什么或不理解,就会对此感兴趣。
假设您有一个对象文字,如:
let someObj = {
"key1": "value1",
"key2": "value2"
};
您尝试像这样访问它:
let value = someObj['not a valid key'];
如果noImplicitAny标志处于关闭状态,则字符串文字将隐式为any,并且不会对键进行任何检查-启用该标志后,您将准确地得到预期的错误。
因此,您认为关闭标记后,仍然可以这样做以使新代码获得安全性:
let value = someObj['not a valid key' as keyof typeof someObj];
但是上面没有产生任何错误,如果再打开该标志,它还会说:“值隐式具有任何类型,等等...”。那么,当我明确断言它应该是什么时,为什么TypeScript仍会隐式将其转换为any
?
如果使用变量,它会按预期工作,那么为什么不使用Type Assertion?
let key: keyof typeof someObj = 'not valid key'; // Compile error just like you'd expect.
另一个注意事项-如果您尝试使用其他类型(非字符串)的类型声明,则会导致类似"Conversion of type 'number' to type '"key1" | "key2"' may be a mistake..."
的错误-因此它清楚地知道应该传递什么,但似乎不知道该传递什么尊重{我认为)keyof T
应该如何保护无效密钥。
答案 0 :(得分:1)
如果noImplicitAny标志处于关闭状态,则字符串文字将隐式为any,并且不会对键进行任何检查
给出noImplicitAny: true
和赋值
let value = someObj['not a valid key'];
,TS查找由value
得到的类型的显式密钥或合适的索引签名。由于类型没有属性'not a valid key'
,也没有声明的索引签名,因此属性值将隐式变为any
,这违反了noImplicitAny
。如果没有此配置选项,则隐式any
可以。
如果使用变量,它会按预期工作,那么为什么不使用Type Assertion?
因为type assertions和assignments的编译器检查略有不同。对于类似
的作业let key: keyof typeof someObj = 'not valid key'; // Compile error just like you'd expect.
,一种类型兼容性规则是(S = 'not valid key'
,T = 'key1'|'key2'
):
S可以分配给类型T [如果] T是联合类型,而S可以分配给T的至少一个组成类型。
'not valid key'
不可同时分配给'key1'
或'key2'
,因此无法编译。对于type assertions,规则有点different:
在形式为
e的类型断言表达式中,要求e的结果类型可分配给T,或将T分配给widened形式e的结果类型,否则发生编译时错误。结果的类型为T。 类型断言在两个方向上检查分配兼容性。因此,类型断言允许进行可能正确但未知的正确类型转换。
对于type compatibility of unions,如果一个联合的任何给定组成部分与另一种类型兼容,也就足够了(是的,有时候那些“金块”信息深深地散布在github问题中,而不仅是在核心文档)。因此,在分配(T = 'key1'|'key2'
,e = 'not a valid key'
)
let value = someObj['not a valid key' as keyof typeof someObj];
,'key1'
或'key2'
都可以分配给'not a valid key'
的加宽形式(= string
),我们很高兴-没有编译错误!
希望,您仍处于清醒状态,情况有所澄清。
答案 1 :(得分:0)
在此示例中,“ as”的作用是断言“无效的密钥”实际上是“ key1”。 'key2'。可以,因为您可以断言类型是相似类型,子类型还是超类型。将字符串声明为数字不起作用,因为它们不相关。
let value = someObj['not a valid key' as keyof typeof someObj];
as
用于类型声明,您将强制类型检查器假设某些内容,如果您将一个字符串声明为另一个字符串,则它基本上与赋值相同。
回到第一件事...
let value = someObj['not a valid key'];
想象一下这种情况:
let obj = {
key1: 'hi',
key2: 'hello'
};
let key = 'key1';
let f = () => {
key = 'key2';
};
f();
obj[key]; // type any
obj[<keyof typeof obj>key]; // this is a workaround
此处key
是通过编程设置的,无论您的noImpicitAny标志是什么,TypeScript都无法推断类型,但是解决方法告诉TypeScript基本信任您。
要使类型检查器确切地知道您要使用点表示法做什么,则不能以编程方式设置键。 (我知道您的问题是如何不必这样做)
let value = someObj.key3;
现在someObj.key3
不存在,它是未定义的,无论'noImpicitAny'标志如何,类型检查器都将给出错误。