问题
Why does the second call to needsString(var1)
in this .tsx component give a warning, but not the first or third calls? And if I change the code to use var2
instead of var1
(which I think have exactly the same types), I don't get a warning. What gives?
import { useParams } from "react-router-dom";
const DeleteTeams2 = ({ var2 } : { var2: string | undefined})=> {
// use var here so that var1 and var2 get exactly the same types
var { var1 } = useParams();
function needsString(str : string) : string {
return "hello";
}
// I can tell typescript that var1
// definitely is non-null with an if statement
if (! var1)
throw new Error("bad, bad varN");
// This call is warning-free
needsString(var1);
// But JSX doesn't know that var1 is non-null for some reason...
return (
<div
onClick={() => {
// I get a warning from this!
needsString(var1);
}}
>
{ needsString(var1) }
</div>
)
}
When I move the mouse over the definitions of both var1
and var2
, they both seem to have the same type: var varN: string | undefined
.
Due to the if statement, I expect var1
to behave as a string
after the if statement. And indeed it does for var2
, but not for var1
.
But I get a warning from the first call to needsString(var1)
:
TypeScript error in /path/to/foobar.tsx(146,29):
Argument of type 'string | undefined' is not assignable to parameter of type 'string'.
Type 'undefined' is not assignable to type 'string'. TS2345
144 | onClick={() => {
145 | // I get a warning from this!
> 146 | needsString(var1);
| ^
147 | }}
148 | >
149 | { needsString(var1) }
Edit: Removed reference to Non-null assertion operator as I was not using a Non-null assertion operator as several comments have pointed out.
回答1:
The issue is that the code here:
onClick={() => {
// I get a warning from this!
needsString(var1);
}}
will not be executed at the same time as this code:
if (! var1)
throw new Error("bad, bad varN");
// This call is warning-free
needsString(var1);
Moreover, var1
is a mutable reference, so from TypeScript's perspective it might change between checking if it's empty you actually using it. For example consider this sample code:
//it's populated
var myVar = "hello";
//verify it's populated
if(!myVar) throw Error("WRONG!");
//absolutely positively `myVar` is populated
//set this to execute later
setTimeout(() => console.log("myVar", myVar));
//change it
myVar = undefined;
So, TypeScript can only determine that var1
might be reassigned. The useParams()
function returns an object typed as
{ [K in keyof Params]?: string | undefined; }
which means that the possible type for var1
is just string | undefined
. If the value changes (TypeScript has no way to determine if it does or not), then it can see that undefined
is not allowed.
Use const
You can simple declare it as const { var1 }
(Playground Link) which makes its type string
only as it assures the compiler you'd not change it between declaration and usage.
Type assertion
Alternatively, explicitly set the type to only string
: var { var1 } = useParams() as { var1: string };
Playground Link. The compiler would accept that maybe you've reassigned it but it would at least know that the only possible thing you would change the variable to is another string. Which satisfies the type used later and would thus work. However, that requires a type assertion and as always, it relies on you being sure this would be the case, otherwise you might get a runtime error that the TypeScript compiler cannot see (because you've "blinded" it, intentionally)
const
only after a check
If you feel uneasy setting var1
to a constant for whatever reason - either you change it or anything else, then you can just do your normal check for var1
and then set a new constant:
if (! var1)
throw new Error("bad, bad varN");
const checkedVar1 = var1;
/* ... */
onClick={() => {
// I get a warning from this!
needsString(checkedVar1);
}}
Playground Link
This still works and will satisfy the TypeScript compiler that checkedVar1
(I assume you'd name it something more sensible) is not changed between definitely being being assigned something that's definitely a string and when it's actually used.
来源:https://stackoverflow.com/questions/60815482/why-does-this-give-a-typescript-warning