Skip to content

Misleading error message when generic type alias is illegally circular #33868

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
samchon opened this issue Oct 8, 2019 · 6 comments
Closed
Labels
Bug A bug in TypeScript Domain: Error Messages The issue relates to error messaging
Milestone

Comments

@samchon
Copy link

samchon commented Oct 8, 2019

Summary

A generic type says "I'm not a generic type".

  • TypeScript Version: 3.7.0-dev.20191006

Code occuring the bug

I tried to make a generic type converting to be primitive for the JSON message. The type Primitive would convert an object type to be primitive. All methods defined in the object type (class) would be erased. If the class has a toJSON() method, Primitive would select its return type.

interface IJsonable<T>
{
    toJSON(): T;
}

type Primitive<Instance> = value_of<Instance> extends object
    ? Instance extends IJsonable<infer Raw>
        ? Primitive<Raw>
        : PrimitiveObject<Instance>
    : value_of<Instance>;

type PrimitiveObject<Instance> =
{
    [P in keyof Instance]: Instance[P] extends Function
        ? never
        : Primitive<Instance[P]>
};

type value_of<Instance> = 
    Instance extends Boolean ? boolean
    : Instance extends Number ? number
    : Instance extends String ? string
    : Instance extends BigInt ? bigint
    : Instance;

The above is my implementation code in my first try. However, a compile error has occured:

src/index.ts:6:6 - error TS2456: Type alias 'Primitive' circularly references itself.

6 type Primitive<Instance> = value_of<Instance> extends object
       ~~~~~~~~~

src/index.ts:8:11 - error TS2315: Type 'Primitive' is not generic.

8         ? Primitive<Raw>
            ~~~~~~~~~~~~~~

src/index.ts:16:11 - error TS2315: Type 'Primitive' is not generic.

16         : Primitive<Instance[P]>
             ~~~~~~~~~~~~~~~~~~~~~~


Found 3 errors.

Detour method, but...

To avoid the bug, I wrote another implementation code for the IJsonable<Raw>. However, I think it's a typical vulnerable and duplicated implementation. I hope the bug to be fixed.

interface IJsonable<T>
{
    toJSON(): T;
}

type Primitive<Instance> = value_of<Instance> extends object
    ? Instance extends IJsonable<infer Raw>
        ? Raw extends object
            ? PrimitiveObject<Raw>
            : value_of<Raw>
        : PrimitiveObject<Instance>
    : value_of<Instance>;

type PrimitiveObject<Instance> =
{
    [P in keyof Instance]: Instance[P] extends Function
        ? never
        : Primitive<Instance[P]>
};

type value_of<Instance> = 
    Instance extends Boolean ? boolean
    : Instance extends Number ? number
    : Instance extends String ? string
    : Instance extends BigInt ? bigint
    : Instance;
@samchon samchon changed the title Generic type says "I am not generic" Error: Generic type says "I am not generic" Oct 8, 2019
@jack-williams
Copy link
Collaborator

This is working as intended. The alias Primitive makes an unguarded recursive reference in its definition. The real error is:

src/index.ts:6:6 - error TS2456: Type alias 'Primitive' circularly references itself.

The error message

 Type 'Primitive' is not generic.

is a consequence of the first error, but perhaps it's abit misleading.

@jack-williams
Copy link
Collaborator

You can read here for the latest spec on recrusive references: #33050.

@RyanCavanaugh RyanCavanaugh added the Bug A bug in TypeScript label Oct 8, 2019
@RyanCavanaugh RyanCavanaugh added this to the Backlog milestone Oct 8, 2019
@RyanCavanaugh RyanCavanaugh changed the title Error: Generic type says "I am not generic" Misleading error message when generic type alias is illegally circular Oct 8, 2019
@jack-williams
Copy link
Collaborator

What would be the proposed fix here? Removing the error message completely, or updating it to something like: Type 'Primitive' is circularly referenced in its definition and cannot be applied to type arguments.

@jcalz
Copy link
Contributor

jcalz commented Oct 9, 2019

The flip side is also strange:

type Z<T> = Z;
//   ~      ! <-- no error here
//   ^-- Type alias 'Z' circularly references itself.

My (possibly naive) expectation here is that Z should still require a type parameter when used, even if the definition is illegally circular, so the bare Z on the right should be flagged as an error also.

@samchon
Copy link
Author

samchon commented Oct 11, 2019

Well, two implementation codes are essentially same. Two codes are all using the Pritimive type recursively. Only different between them are the way to handling the Raw type.

Really it's the intended spec? not bug?

Error Code

type Primitive<Instance> = value_of<Instance> extends object
    ? Instance extends IJsonable<infer Raw>
        ? Primitive<Raw>
        : PrimitiveObject<Instance>
    : value_of<Instance>;

type PrimitiveObject<Instance> =
{
    [P in keyof Instance]: Instance[P] extends Function
        ? never
        : Primitive<Instance[P]>
};

Detour Code

type Primitive<Instance> = value_of<Instance> extends object
    ? Instance extends IJsonable<infer Raw>
        ? Raw extends object
            ? PrimitiveObject<Raw>
            : value_of<Raw>
        : PrimitiveObject<Instance>
    : value_of<Instance>;

@RyanCavanaugh RyanCavanaugh added the Domain: Error Messages The issue relates to error messaging label Dec 18, 2019
@RyanCavanaugh
Copy link
Member

No longer an error as of 5.3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Domain: Error Messages The issue relates to error messaging
Projects
None yet
Development

No branches or pull requests

4 participants