Skip to content

Type inference/IntelliSense mismatch and incorrect emit of "any" in declaration #19565

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

Closed
magnushiie opened this issue Oct 30, 2017 · 4 comments
Labels
Bug A bug in TypeScript
Milestone

Comments

@magnushiie
Copy link
Contributor

TypeScript Version: 2.7.0-dev.20171029

Code
This is from code using the https://github.com/pelotom/runtypes package, I've tried to make it as self-contained as possible, but it's still quite long, sorry.

export interface Runtype<A> {
    _falseWitness: A;
}

export interface Record<O extends {
    [_: string]: Runtype<{}>;
}> extends Runtype<{
    [K in keyof O]: O[K]['_falseWitness'];
}> {
    tag: 'record';
    fields: O;
}

const Record = <O extends {
    [_: string]: Runtype<{}>;
}>(fields: O): Record<O> => ({
    tag: 'record',
    fields,
    _falseWitness: undefined as any,
});

const CompanyData = Record({});

const Company = Record({
    companyData: CompanyData,
});

export const CompanyWrapper = Record({
    company: Company,
});
CompanyWrapper; // correctly inferred to be Record<{ company: Record<{ companyData: Record<{}>; }>; }>;
CompanyWrapper._falseWitness; // incorrectly inferred to be { company: any; }
CompanyWrapper._falseWitness.company.companyData;
// CompanyWrapper._falseWitness.company.x; // this would correctly error even given the incorrect inferred type above

export interface APICall<Response> {
    readonly ResponseType: Runtype<Response>;
}

export const companyCall: APICall<typeof CompanyWrapper['_falseWitness']> = { ResponseType: CompanyWrapper };
export const companyAPI = {
    getCompany: companyCall,
};

Expected behavior:

  1. getCompany is emitted with the correct type (APICall<Record<{ company: Record<{ companyData: Record<{}>; }>; }>>) in the output declaration
  2. _falseWitness member is inferred to be the correct type in IntelliSense (see comments inline in code above)

Actual behavior:
The following declaration is emitted - company member in getCompany is inferred to be any.

export interface Runtype<A> {
    _falseWitness: A;
}
export interface Record<O extends {
    [_: string]: Runtype<{}>;
}> extends Runtype<{
    [K in keyof O]: O[K]['_falseWitness'];
}> {
    tag: 'record';
    fields: O;
}
export declare const CompanyWrapper: Record<{
    company: Record<{
        companyData: Record<{}>;
    }>;
}>;
export interface APICall<Response> {
    readonly ResponseType: Runtype<Response>;
}
export declare const companyCall: APICall<typeof CompanyWrapper['_falseWitness']>;
export declare const companyAPI: {
    getCompany: APICall<{
        company: any;
    }>;
};

This happens even with noImplicitAny where no any types should be introduced.

Workaround:

There is a workaround (although very annoying to remember to do as this introduction of any is often not easy to detect and bugs can be introduced) - wrap the CompanyWrapper type in an interface:

export declare type Static<A extends Runtype<{}>> = A['_falseWitness'];
export interface CompanyWrapper extends Static<typeof CompanyWrapper> { }
export declare const companyCall: APICall<CompanyWrapper>;
export declare const companyAPI: {
    getCompany: APICall<CompanyWrapper>;
};
@gcnew
Copy link
Contributor

gcnew commented Oct 30, 2017

I think this is another manifestation of #18754. To cut-off inlining of recursive alias references the type serialiser substitutes them with any.

@magnushiie
Copy link
Contributor Author

It could be, although when considering the exact specialized types, there's no recursion here. On generic level of course Record <O> is recursive.

@gcnew
Copy link
Contributor

gcnew commented Oct 30, 2017

You are right, it looks like a different issue. I didn't read the description carefully and was looking only at the unexpected any.

@RyanCavanaugh
Copy link
Member

This appears to be fixed now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript
Projects
None yet
Development

No branches or pull requests

4 participants