打字稿:匿名函数中可能未定义的变量
Typescript: variable possibly undefined inside anonymous function
TLDR;在匿名函数中使用变量之前检查变量仍然 TS 警告变量可能未定义
在下面的代码示例中,检查变量 baseDirId
是否未定义,然后传递给 array.map 函数,但 TS 警告 baseDirId
可以未定义。
const rstr = async (a: string) => {
return a + "bleh"
}
const args: { baseDirId: string | undefined } = {
baseDirId: "someid"
// baseDirId: undefined
}
const someArr = ["bleh1", "bleh2"]
const func1 = async (): Promise<void> => {
try {
// Assume baseDirId can be undefined
let baseDirId = args.baseDirId
// Trigger if baseDirId is undefined
if (!baseDirId) {
const baseDirObj = { id: "valid string" }
baseDirId = baseDirObj.id
}
console.log(typeof baseDirId)
// baseDirId cant be anything other than a string
if (typeof baseDirId !== "string") {
return Promise.reject("Base Dir response invalid")
}
// Why is baseDirId `string | undefined` inside rstr call below even after above checks
const bleharr = someArr.map((val) => rstr(baseDirId))
console.log(await Promise.all(bleharr))
} catch (err) {
console.error(err)
}
}
func1().catch(err => console.error(err))
有没有可能 baseDirId
可以是 undefined
的情况?
为什么 TS 不允许呢?更好的方法?
TypeScript 不会检查类型,甚至不会在运行时更改类型,因此 baseDirId
类型将始终是 string | undefined
除非您对类型进行窄类型或其他操作,因此您可以有很多选择试试。
1.使用默认
let baseDirId = args.baseDirId || "valid string"
2。进行条件值检查
if(args.baseDirId){
let baseDirId = args.baseDirId
// ...
// do something you want
}
但是你不能直接在下面的代码片段中这样做,因为你使用 let
来声明 baseDirId
,然后它就不会工作,因为它可以更改为 undefined
除非通过 const
声明
if(baseDirId){
const bleharr = someArr.map((val) => rstr(baseDirId))
}
3。使用 !
non-null assertion operator
当您确定它一定存在并且不想更改任何其他内容时
const bleharr = someArr.map((val) => rstr(baseDirId!))
让我们将代码稍微更改为
return () => someArr.map((val) => rstr(baseDirId))
因此,与其直接调用 .map
,它可能会在稍后的某个时间点调用 运行。与此同时,其他一些代码可能已将 undefined 写入 baseDirId
。因此,为了正确推断类型,Typescript 必须检查是否有其他代码在某个时候覆盖了该变量。这是一项相当复杂的任务(在某些极端情况下甚至是不可能的)。如果我们的内部函数在多个地方被调用,这也会变得更加复杂:
let mutable: string | undefined = /*...*/;
const inner = () => fn(mutable); // << how to infer this?
mightCall(inner); // if called back here, mutable would be "string | undefined"
if(typeof mutable === "string") mightCall(inner); // if called here, mutable could be narrowed down to "string", but only if mightCall calls back synchronously
mutable = undefined; // if mightCall calls back asynchronously, 'mutable' would have to be inferred as undefined
因此,当函数从外部作用域访问变量时,Typescript 编译器假定尽可能宽的类型。类型缩小仅适用于函数体本身。要缩小类型,您需要类型断言,或者将值复制到 const
:
let mutable: string | undefined = /*...*/;
if(typeof mutable !== "string") return;
// mutable get's narrowed down to string here due to the typeof type guard
const immutable = mutable;
// ^ inferred as string, this is the widest possible type
TLDR;在匿名函数中使用变量之前检查变量仍然 TS 警告变量可能未定义
在下面的代码示例中,检查变量 baseDirId
是否未定义,然后传递给 array.map 函数,但 TS 警告 baseDirId
可以未定义。
const rstr = async (a: string) => {
return a + "bleh"
}
const args: { baseDirId: string | undefined } = {
baseDirId: "someid"
// baseDirId: undefined
}
const someArr = ["bleh1", "bleh2"]
const func1 = async (): Promise<void> => {
try {
// Assume baseDirId can be undefined
let baseDirId = args.baseDirId
// Trigger if baseDirId is undefined
if (!baseDirId) {
const baseDirObj = { id: "valid string" }
baseDirId = baseDirObj.id
}
console.log(typeof baseDirId)
// baseDirId cant be anything other than a string
if (typeof baseDirId !== "string") {
return Promise.reject("Base Dir response invalid")
}
// Why is baseDirId `string | undefined` inside rstr call below even after above checks
const bleharr = someArr.map((val) => rstr(baseDirId))
console.log(await Promise.all(bleharr))
} catch (err) {
console.error(err)
}
}
func1().catch(err => console.error(err))
有没有可能 baseDirId
可以是 undefined
的情况?
为什么 TS 不允许呢?更好的方法?
TypeScript 不会检查类型,甚至不会在运行时更改类型,因此 baseDirId
类型将始终是 string | undefined
除非您对类型进行窄类型或其他操作,因此您可以有很多选择试试。
1.使用默认
let baseDirId = args.baseDirId || "valid string"
2。进行条件值检查
if(args.baseDirId){
let baseDirId = args.baseDirId
// ...
// do something you want
}
但是你不能直接在下面的代码片段中这样做,因为你使用 let
来声明 baseDirId
,然后它就不会工作,因为它可以更改为 undefined
除非通过 const
if(baseDirId){
const bleharr = someArr.map((val) => rstr(baseDirId))
}
3。使用 !
non-null assertion operator
当您确定它一定存在并且不想更改任何其他内容时
const bleharr = someArr.map((val) => rstr(baseDirId!))
让我们将代码稍微更改为
return () => someArr.map((val) => rstr(baseDirId))
因此,与其直接调用 .map
,它可能会在稍后的某个时间点调用 运行。与此同时,其他一些代码可能已将 undefined 写入 baseDirId
。因此,为了正确推断类型,Typescript 必须检查是否有其他代码在某个时候覆盖了该变量。这是一项相当复杂的任务(在某些极端情况下甚至是不可能的)。如果我们的内部函数在多个地方被调用,这也会变得更加复杂:
let mutable: string | undefined = /*...*/;
const inner = () => fn(mutable); // << how to infer this?
mightCall(inner); // if called back here, mutable would be "string | undefined"
if(typeof mutable === "string") mightCall(inner); // if called here, mutable could be narrowed down to "string", but only if mightCall calls back synchronously
mutable = undefined; // if mightCall calls back asynchronously, 'mutable' would have to be inferred as undefined
因此,当函数从外部作用域访问变量时,Typescript 编译器假定尽可能宽的类型。类型缩小仅适用于函数体本身。要缩小类型,您需要类型断言,或者将值复制到 const
:
let mutable: string | undefined = /*...*/;
if(typeof mutable !== "string") return;
// mutable get's narrowed down to string here due to the typeof type guard
const immutable = mutable;
// ^ inferred as string, this is the widest possible type