-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Sub-optimal type parameter inference with strictFunctionTypes enabled #52111
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
I think everything works if you slightly tweak my suggestion here to instead introduce an overload with three type parameters to handle cases where the input and output types share a supertype but aren't otherwise related: declare function assertNode<T extends Node, U extends T>(node: T | undefined, test: (node: T) => node is U): asserts node is U;
declare function assertNode<T extends N, U extends N, N extends Node>(node: T | undefined, test: (node: N) => node is U): asserts node is U;
declare function cast<TOut extends TIn, TIn = any>(value: TIn | undefined, test: (value: TIn) => value is TOut): TOut;
declare function cast<TOut extends T, TIn extends T, T>(value: TIn | undefined, test: (value: T) => value is TOut): TOut; Here's a modified example where everything appears to work as expected. |
This is the workaround I mentioned that I tried and reverted; if you look at that playground, you'll see the assertion function has an error on its return because it doesn't like the relationship of the input and output. The change in cast also has the unfortunate side effect of letting you cast anything to anything else, since it'll happily choose unknown/any as a supertype: declare function isNumber(x: unknown): x is number;
declare const nodeArray: NodeArray<Node>;
const x = cast(nodeArray, isNumber);
// ^? number Which is "fine" because it'll just throw, but does mean you can't know if you're attempting to cast to a totally unrelated type. |
First a simplified example: interface A { a: string }
interface B extends A { b: string }
interface C extends A { c: string }
declare function cast<T, U extends T>(x: T, test: (x: T) => x is U): U;
declare function isC(x: A): x is C;
function f1(a: A, b: B) {
const x1 = cast(a, isC); // cast<A, C>
const x2 = cast(b, isC); // cast<A, C>
} Currently, the I'm going to put up a PR that adds this extra consideration to type inference. |
These are cases I've extracted from #49929; they are cases where if you don't specify the type parameters yourself, the inference isn't good.
This is potentially a full duplicate of #49924, however, the fix suggested in that issue does not work properly in our codebase. To test these, it may be easiest to just clone #49929 with the explicit type parameters removed and see what works and what doesn't.
Output
Compiler Options
Playground Link: Provided
The text was updated successfully, but these errors were encountered: