I struggle a bit with one common JavaScript pattern during using TypeScript. It's about:
- declaring some "let" variable without setting to it any initial value
- set this value to the variable in some callback
- work with this variable after that callback was executed
Here's code example:
const wait = (cb: Function) => // just example of a possible callback
new Promise((resolve) =>
setTimeout(() => {
cb();
resolve();
}, 1)
);
async function v1() {
let a: { bool: boolean };
await wait(() => {
a = { bool: true }; // from sinse `a` isn't empty
});
alert(a); // error: Variable 'a' is used before being assigned. ts(2454)
if (a === undefined) return; // error: Variable 'a' is used ...
alert(a); // only now it's okay: { bool: true }
}
As you can see:
- TypeScript understands that
a
may not be initialized
- but at the same time TS doesn't understand that it may be initialized
Ok. I what if I will just add some check and possibility to be null
:
async function v2() {
let a: { bool: boolean } | null = null;
await wait(() => {
a = { bool: true };
});
alert(a); // no error
alert(a.bool); // error: possibly null
if (a === undefined || a === null) return;
alert(a.bool); // error: Property 'bool' does not exist on type 'never' ts(2339)
}
So now TypeScript knows that it's not nil. So... it must be { bool: boolean }
. But... TypeScript thinks that it's unreachable code branch, so the type is never
.
Is there any simple reasonable solution to persuade TypeScript that the code is correct and the proper type is { bool: boolean }
?
E.g.:
- I can set not-null initial value. But actually usually it's hardly possible (cause there's much more sophisticated types)
- I can use
// @tsignore
, as MyType
, !
, ignore linter rules
These ^ don't look well to me :) I think I miss something important.
My tsconfig:
{
"compilerOptions": {
"sourceMap": true,
"module": "commonjs",
"moduleResolution": "node",
"esModuleInterop": true,
"target": "ES2019",
"allowJs": false,
"checkJs": false,
"strict": true,
"resolveJsonModule": true,
"lib": ["ES2019", "DOM"],
"types": ["jest", "node"],
"typeRoots": ["./src/types", "./node_modules/@types"],
"outDir": "./build",
"baseUrl": "./src/",
"paths": { "~/*": ["*"] }
},
...
}
See Question&Answers more detail:
os 与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…