-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Suggestion: Type narrowing also narrows conditional types #21879
Comments
I think this might be related, but it's reversed -- using a conditional mapped type to narrow the original. declare function log(s: string): void;
type Transforms<T, S> = {
[K in keyof T]: T[K] extends S ? (undefined | ((d: T[K]) => S)) : ((d: T[K]) => S);
}
function logData<T>(d: T, keys: (keyof T)[], transforms: Transforms<T, string>): void {
keys.forEach((k) => {
const t = transforms[k];
const dk = d[k];
if (t) log(t(dk));
else log(dk); // Argument of type 'T[keyof T]' is not assignable to parameter of type 'string'.
});
} |
Updated this quite a bit: the example I had was poor, the |
If I've read correctly, I think this is working as intended; in the general case, the type of function compare<T>(x: T, y: T) {
if (typeof x === "string") {
y.toLowerCase() // appropriately errors; 'y' isn't suddenly also a 'string'
}
// ...
}
// why not?
compare<string | number>("hello", 100); |
Ah right, I see what you mean. But this is a shortcoming of TypeScript as well, because the actual definitions I've used cannot cause that problem. The issue is that TS doesn't recognize that Some notion of that sort of mutual exclusivity also seems to be the primary impediment to #20375, which also came up in this example. If we had that, both of these problems would be tractable. Furthermore, in this case, I don't even want For example, what I would have liked was function mapFooOrBar<R>(
foobar: Foo | Bar,
mapFoo: typeof foobar extends Foo ? ((value: Foo) => R) : ((impossible: never) => never),
mapBar: typeof foobar extends Bar ? ((value: Bar) => R) : ((impossible: never) => never),
): R { but of course, Would suggestions of this mutual exclusivity notion and/or this delayed |
Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed. |
TypeScript Version: 2.8.0-dev.20180204
Code
(the casting
foobar as never
for thebroke
function is a workaround for #20375)Expected Behavior
Compile without errors.
Actual Behavior
The types of
mapFoo
andmapBar
are not reconsidered in light of passing the typeguard:Cannot invoke an expression whose type lacks a call signature. Type '((value: Foo) => R) | ((impossible: never) => never)' has no compatible call signatures.
Cannot invoke an expression whose type lacks a call signature. Type '((value: Bar) => R) | ((impossible: never) => never)' has no compatible call signatures.
My use case for this is a situation where I have a union of three types, but some cases will only ever use a union of two of those types. I want this kind of utility function to be re-usable for those situations, but indicate that the handlers for unincluded cases will never be called (and allow us to type-safely stub those branches, e.g. with that
broke
function).Our project uses these kinds of unions and utility functions a lot and this would significantly improve them.
The text was updated successfully, but these errors were encountered: