-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Detect symbol-named properties as union discriminants #36463
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
My specific usecase is to determine if a value is an |
first your example would be a little clearer if you just declare const m: m | b;
declare function f(v: b): void; second you don't need to use symbols to demonstrate this behaviour: interface A {
x: undefined;
y: number;
}
interface B {
x: number;
y: number;
}
declare const val: A | B
declare function f(v: B): void
if( "x" in val) f(val); And the reason typescript doesn't narrow the type in this case is because let test = {
x: undefined,
[Symbol.iterator]: undefined
}
console.log("x" in test); // true
console.log(Symbol.iterator in test) // true This is why checking for just the presence of a field never narrows types in typescript since typescript is never sure what extra fields might exist at runtime. |
You probably want to use a type assertion function: function isAsyncGen<T>(x: T): x is Extract<T, AsyncIterable<any>>{
return (Symbol.asyncIterator in x) && x[Symbol.asyncIterator];
}
declare const x: string[] | AsyncIterable<string>
if(isAsyncGen(x)){
// here we know x is AsyncIterable<string>
} else {
// here we know x is string[]
} |
hi @tadhgmister thanks for pointing that out! I set them to I'm aware that this problem can be solved with type guards, but I don't think that makes this any less of a bug! |
I don't think this is a bug, when strict null check is disabled it is totally valid for any variable to be declare const val: {a: string | number};
declare function f(arg: {a: string}):void
// Argument of type '{ a: string | number; }' is not assignable to parameter of type '{ a: string; }'.
// still get error even though we know .a is a string.
if(val.a == "hello") f(val);
// this is allowed because val.a is narrowed.
if(val.a == "world") f({a: val.a})
// if the union is differentiated by constant values (like number literals) then union is narrowed
declare const bar: {a: string, c: 0} | {a: number, c: object}
// bar is narrowed here because it checks a field that has constant type
if(bar.c == 0) f(bar); These are the kinds of cases where type guard functions are designed for. :) |
I'm experiencing the same behaviour, intuitively this seems like a bug. The compiler should have enough information to narrow the types.
|
Oh ok now I see the issue. I do notice that if you change it to |
I think I'm experiencing this issue (using JS). Something like this trivial example: const kName = Symbol('name');
class Frog {
/**
* @param {string} name - Name of frog
*/
constructor(name) {
this[kName] = name; // Element implicitly has 'any' type because expression of type 'symbol' can't be used to index type 'Frog'
}
get name() {
return this[kName]; // Element implicitly has 'any' type because expression of type 'symbol' can't be used to index type 'Frog'
}
} A workaround is something horrid like: /** @type {string} */
get name() {
return Reflect.get(this, kName);
} |
try using const kFoo = Symbol('name');
class Frog {
/**
* @param {string} name - Name of frog
*/
constructor(name) {
this[kFoo] = name;
}
get name() {
return this[kFoo];
}
} |
I think this suggestion is a specific case of #36230. If we let constant property access narrow types then it would automatically extend to allow symbols for discrimination. |
@tadhgmister Sorry, I was inconsistent in that example. I've updated it to reflect the actual code. It is a problem either way |
@boneskull You are still experiencing a different problem, if the error says
This is the same issue with having |
This was fixed in #36230 |
TypeScript Version: 3.7.5
Search Terms:
discriminated union symbol, AsyncIterator, Iterator.
Expected behavior:
Unions discriminated by symbol members should be narrowable.
Actual behavior:
No narrowing occurs.
Code
Output
Compiler Options
Playground Link: Provided
The text was updated successfully, but these errors were encountered: