-
Notifications
You must be signed in to change notification settings - Fork 12.8k
TypeScript fails to narrow out undefined
via typeof
check on generic indexed access
#50548
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
This doesn't seem to have much to do with const getStatFromSet = function <K extends keyof PlanetaryBodiesMap>(
statSet: PlanetaryBodiesMap[K],
statName: keyof typeof statSet,
): number {
return statSet[statName]; // error
} This feels like #32365 to me. |
>3-year-old #32365 looks related and may have something to do with an underlying reason; it's possible that fixing that issue could also fix this one. However, I think it's quite possible this issue could also be fixed without fixing that one by focusing on the elimination of 'undefined' from the type of any constant that is inside a conditional eliminating undefined from its type. |
Not seeing how, given that the following still fails: const getStatFromSet = function <K extends keyof PlanetaryBodiesMap>(
statSet: Partial<PlanetaryBodiesMap[K]>,
statName: keyof typeof statSet,
): number | undefined {
return statSet[statName]; // error!
} If |
The core expected behavior I'm trying to call attention to in this bug report is: const x = //any process at all by which x gets a value and type, even if that's a buggy process
if(typeof x !== 'undefined') {
//**Regardless of how x was defined,**
//whether using the assignment example from the OP here or any other,
//any attempt to use it here should NOT result in an error that "x could be `undefined`".
} If fixing #32365 would allow |
undefined
in conditional checking for thatundefined
via typeof
check on generic indexed access
Yeah, I think there is something weird with interface Foo {
a: { b: string }
}
function f<K extends keyof Foo>(f: Partial<Foo[K]>, k: keyof Foo[K]) {
const x = f[k];
if (typeof x !== "undefined") {
let _: {} = x; // Should error because of possible `null`, but errors because of `undefined`
}
const y = f[k];
if (y != undefined) {
let _: {} = y;
}
x; // This is weird too
y;
} |
Here is what I suspect is another example of the same issue, though it can be broken out to a separate issue if others think it's not a duplicate: //In the motivating example, these types are way more complex than simple constant strings.
interface AnimalSounds { Cat: 'meow'; Dog: 'woof'; Duck: 'quack';}
type AnimalSound = AnimalSounds[keyof AnimalSounds]; //"meow" | "woof" | "quack"
export const callerFn = function<A extends keyof AnimalSounds> (
animalTypeName: A,
sonicEnvironment: Partial<AnimalSounds> //cannot be restricted to require A here
) {
const sound = sonicEnvironment[animalTypeName];
if (typeof sound === 'undefined') {
throw new Error('Could not find sound in environment.');
}
//At/after this line, 'sound' should be narrowed to EXCLUDE the 'undefined' type possibility.
//i.e. sound should be Exclude<Partial<AnimalSounds>[A], undefined>, but the exclusion isn't working.
//You can move the error and DRY up casting by using a narrowing const, but it's still an error:
const soundNarrowed: Exclude<Partial<AnimalSounds>[A], undefined> = sound; //Type 'undefined' not assignable
//Error in first parameter of next line:
//Argument of type 'Partial<AnimalSounds>[A]' is not assignable to parameter of type 'AnimalSounds[A]'.
//Type '"meow" | undefined' is not assignable to type '"meow"'.
//Type 'undefined' is not assignable to type '"meow"'.ts(2345)
calledFn<AnimalSounds[A]>(sound, toUpperCaseTyped(sound));
//In the line below but NOT above, the 'undefined' possibility is correctly narrowed out:
calledFnNoGenericOneParam(sound);
}
const calledFn = function<S extends AnimalSound>(sound: S, loudSound: Uppercase<S>) {/*...*/};
//Dropping the generic doesn't work in context,
//because multiple types in the function are derived from the generic type parameter.
const calledFnNoGenericOneParam = function(sound: AnimalSound) {/*...*/};
//Bonus issue(#44268): the cast in the return statement of the next line should be automatic & unnecessary:
const toUpperCaseTyped = function<S extends string>(strIn: S) {return strIn.toUpperCase() as Uppercase<S>;}; With this example, the behaviour changed between 4.2.3 and 4.3.5, so that the parameter to |
Bug Report
π Search Terms
undefined narrowed 2322
π Version & Regression Information
β― Playground Link
Playground link with relevant code
π» Code
π Actual behavior
TypeScript complains that a constant value can be
undefined
within a conditional block where that is already checked for and excluded.π Expected behavior
undefined
, TypeScript excludes| undefined
from the possible types of the constant.| undefined
, which matters. In the unsimplified version where the type is more complex, the type of the constant shown on hover is very linguistically different from the type shown in the error, making this issue harder to debug.The text was updated successfully, but these errors were encountered: