-
Notifications
You must be signed in to change notification settings - Fork 12.8k
TS 3.4: Error when passing dynamically imported generic type as a function argument #30712
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
Are there any suggested workarounds for the above in the meantime? I would like to use the incremental build features from 3.4, but hitting this issue with some dynamic imports |
Inference for interface Promise<T> {
then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;
} The type we want is This is arguably correct since To get the desired type we could break interface Promise<T> {
then<TResult1 = T>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null): Promise<TResult1>;
then<TResult1 = T, TResult2 = never>(onfulfilled: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;
} Or we could modify return type inference to only apply to type parameters that have inferences from some argument, not just the return type. I think this would break some existing uses but they might be good breaks. @rbuckton @ahejlsberg can you take a look to see whether you would rather
|
Workarounds:
|
Here's a much smaller repro that doesn't depend on React or Promise: declare class Component<P> { }
interface ComponentClass<P = {}> {
new (props: P, context?: any): Component<P>;
}
declare function myFunction<TProps>(loader: P<ComponentClass<TProps>>): void;
export class Page extends Component<{}> {}
interface P<T> {
then<T1=T, T2=never>(pass?: ((value: T) => T1), fail?: ((reason: any) => T2)): P<T1 | T2>;
}
declare var pp: P<{ Page: typeof Page }>
myFunction(pp.then(m => m.Page))
const loader = pp.then(m => m.Page)
myFunction(loader) Edit: even shorter: declare function myFunction<U>(loader: P<(u: U) => void>): void;
interface P<V> {
then<T1, T2>(pass: ((value: V) => T1), fail?: ((reason: any) => T2)): P<T1 | T2>;
}
declare var pp: P<(x: {}) => void>
myFunction(pp.then(m => m)) |
@ahejlsberg tracked this down a sentinel type, silentNever, possibly incorrectly escaping inference. @ahejlsberg do you want to take over on this bug or do you want me to finish it up? |
Even smaller: interface Prom<T> {
then<T1, T2>(pass?: ((value: T) => T1), fail?: ((reason: any) => T2)): Prom<T1 | T2>;
}
declare function myFunction<T>(loader: () => Prom<(x: T) => void>): T;
declare let pp: Prom<(x: number) => void>;
const loader: () => Prom<(x: number) => void> = () => pp.then(m => m);
myFunction(() => pp.then(m => m)); // Error |
Yep, no question #29847 is an improvement. This bug is just exposed by it, I think. Here's what happens in a bit more detail: when inferring type arguments for const outerMapper = getMapperFromContext(cloneInferenceContext(getInferenceContext(node), InferenceFlags.NoDefault));
const instantiatedType = instantiateType(contextualType, outerMapper); After that, we are inferring from Removing |
There are two classes of test failures:
I think this means that the bug was technically caught two years ago in #13487, when we tweaked the definition of Promise and incorrectly accepted baselines. We just didn't notice it among the 1500 lines of churn for promiseType.ts. |
@ahejlsberg I'm not sure if the correct fix is to change NoDefault to return silentNeverType less often or to change inference downstream to skip silentNever in more cases. Opinions? I think the second is probably a smaller change, so I'll look at that first. |
I tried returning nonInferrableType since I believe I want propagation of non-inferrability, but it didn't actually propagate through anonymous type instantiation. I added that and then I get a different error: error TS2345: Argument of type 'P<unknown>' is not assignable to parameter of type 'P<(u: unknown) => void>'.
Type 'unknown' is not assignable to type '(u: unknown) => void'.
myFunction(pp.then(m => m))
~~~~~~~~~~~~~~~ It looks like I've prevented any inference from happening, even the good one from the first parameter of @ahejlsberg Do you think that switching NoDefault to return nonInferrableType is the right idea? I feel like I've just made a mistake in the way I'm propagating it in instantiation. |
Moving to 3.6 since we are out of time on 3.5, and it's not affecting a large number of people (and there's an easy workaround or two). |
Any progress? |
@sandersn those workarounds can't be used by library writers but have to be known by the consumers of the library.. it is affecting a large number of people - e.g. everyone who is using nextjs with dynamic imports: https://nextjs.org/docs/advanced-features/dynamic-import could you please consider picking up this issue once again? |
Was lead here experiencing this exact issue. Nothing really useful to say other than that. |
Is still moving from 3.6 up to 5? |
TypeScript Version: 3.4.0-dev.201xxxxx
Search Terms: TS2322, dynamic import, dynamic import never, dynamic import generic
Code
Expected behavior:
No compile error. This was the behavior in TS 3.3 and earlier.
Actual behavior:
There is an error after upgrading to TS 3.4:
Repro here: https://github.com/srmagura/ts-import-repro
The text was updated successfully, but these errors were encountered: