-
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
Type error when using generic function result as argument, but not when assigned to temp variable first #35339
Comments
I don't know if this will help, but I was able to work around this by referencing the type parameter const makeF1p = <T>(t: T) => f1 as Extract<typeof f1 | T, typeof f1>; |
I think we can remove declare const foo: <F extends () => unknown, G extends (x: ReturnType<F>) => unknown>(
f: F, g: G) => any |
Similar case: Playground link const fn = <A, B>({ a, b, c }: {
a: () => A;
b: (a: A) => B;
c: (a: A, b: B) => any;
}) => {
const t = a();
return c(t, b(t));
};
// wrong
fn({
a: () => 1,
b: (a) => 1 + a, // prop b type is (a: number) => unknown
c: (a, b) => a + b, // arg b is unknown
});
// wrong
fn({
a: () => 1,
b: (_) => 1, // prop b type is (a: number) => unknown
c: (a, b) => a + b, // arg b is unknown
});
// correct
fn({
a: () => 1,
b: () => 1, // prop b type is (a: number) => number
c: (a, b) => a + b, // arg b is number
}); |
Smaller repro, pretty sure this is the same issue: declare function foo<T = {}>(): number & T;
declare function bar<T>(x: T): void;
bar(foo()); // T = never
let tmp = foo(); // T = {}
bar(tmp); Contextual typing seems to be causing chained generic calls to influence each others' type parameters. |
I'm not sure if this is a bug (regression) or a (new) feature. It adds one more thing that can make class-factory mixins brittle. F.e., I have to consciously write import {Mixin, MixinResult, Constructor} from 'lowclass'
function SizeableMixin<T extends Constructor>(Base: T) {
const _Base = Constructor(Base) // HERE
const Parent = Observable.mixin(TreeNode.mixin(_Base))
class Sizeable extends Parent { /* ... */ }
return Sizeable as MixinResult<typeof Sizeable, T>
} instead of import {Mixin, MixinResult, Constructor} from 'lowclass'
function SizeableMixin<T extends Constructor>(Base: T) {
const Parent = Observable.mixin(TreeNode.mixin(Constructor(Base))) // HERE
class Sizeable extends Parent { /* ... */ }
return Sizeable as MixinResult<typeof Sizeable, T>
} where the definition of export type Constructor<T = object, A extends any[] = any[], Static = {}> = (new (...a: A) => T) & Static
// this is used for type casting in mixins
export function Constructor<T = object, Static = {}>(Ctor: Constructor<any>): Constructor<T> & Static {
return (Ctor as unknown) as Constructor<T> & Static
} This issue was not happening before (the second example would work), so it seems to be from some relatively new version of TypeScript within the past few months. |
I’m almost positive it’s a bug. Type inference for a generic call shouldn’t be affected by whether the result is assigned to a variable vs. directly passing it to another generic function. |
Deleted my previous comments, I was thinking something else. Yeah in @fatcerberus's example above it seems like the generic args for |
This is a fairly normal consequence of having too many type parameters in the absence of unification. We'd need to have an iterative inference algorithm to process this call as expected. The root problem is that there's a candidate via I would rewrite declare const foo: <R1, R2>(f1: () => R1, f2: (input: R1) => R2) => R2 |
What's the performance trade off of adding an "iterative inference algorithm"? |
declare function foo<T = {}>(): number & T;
declare function bar<T>(x: T): void;
bar(foo()); // T = never
let tmp = foo(); // T = {}
bar(tmp); In this case, there is only a single type parameter involved, but appears to trigger the same issue. Is it actually the same issue, or is this something else? Because I really don't expect the presence--or lack thereof--of an intermediate assignment (which is itself inferred!) to affect the inference of the function call... and yet it does. |
Issue originally discovered by @eyelidlessness on Gitter.
TypeScript Version: 3.8.0-dev.20191123
Search Terms:
generic
returntype
Code
Expected behavior:
Both
foo()
calls should compile as the only difference is whether the result of a function call is assigned to a temporary first.Actual behavior:
The first call to
foo()
, wheremakeF1p()
is called inline, produces a type error at compile time. The second, where the return value ofmakeF1p()
is assigned to temp variable first, compiles fine. The error is:It almost seems like the type parameter inference machinery is leaking outside the scope of the call expression or something? It's weird, at any rate.
Playground Link:
https://www.typescriptlang.org/play/?ts=3.8.0-dev.20191123&ssl=1&ssc=1&pln=33&pc=1#code/CYUwxgNghgTiAEYD2A7AzgF3gMyUgXPADwBQ858ASgIwA0ZFAYtfCAB4YgrBrwAUASngBeAHxU6DcpQBM9CvEYzWHLj34BLFAAcArhkKUQGXTBQAVAJ7aQRZqKFiqMkqL5Sc1Qs3kVsM7xdHcSgUSxISZHQsbBZhfmD+AG94KEIAcih0+ABfAUjUTBxleL4tPQN4FLT4TBgtAHNcxL4UgCMMtuy8iNwkPljaYqEAehH4AHE8YAioooBbKABrEGYRBJFxWIBuXrw+RZXmQSH-UfGppBmC6PhD1eptdaJzN0rzRJ29-o975m0+Ol0gJfOR-PAxvAAKIwGBIGDwADqAAkAJrwfDuAS7G4LKCgdZ-R6A4E4vruBSLUBDSHmAAWGl4jJwWhAHn8JHyQA
Related Issues: I didn't find any, but @jack-williams says #30215 might be the root cause.
The text was updated successfully, but these errors were encountered: