Skip to content

Infered type in generic that is boolean used in conditional type ends up in compiler error #37069

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
atothek1 opened this issue Feb 27, 2020 · 3 comments
Labels
Duplicate An existing issue was already created

Comments

@atothek1
Copy link

atothek1 commented Feb 27, 2020

Dear TS Team,

using infer to extract the type of a generic which is passed to a conditional type ends up reporting error
Argument of type 'true' is not assignable to parameter of type 'never'.(2345) but only for boolean.

The returned type is a union FactoryReturnAFn<false> | FactoryReturnAFn<true> instead of
FactoryReturnAFn<boolean>;

passing then true or false as parameter leads to compile error.

TypeScript Version: 3.7.x, 3.8.x, nighly

Search Terms: infer, conditional, generic, boolean

Code

interface BaseType<T = undefined> {
    readonly data: T;
}

// infered types
type ExtractType<T> = T extends BaseType<infer A> ? A : undefined;

// define type definitions for function signatures that the factory can return
type FactoryReturnAFn<T> = (p: T) => BaseType<T>;
type FactoryReturnBFn<T> = (p: T) => BaseType<number>;

// define a conditional type
type ConditionalFactoryReturnFn<T> = T extends undefined
    ? FactoryReturnBFn<T>
    : FactoryReturnAFn<T>;

function factory<T extends BaseType<ExtractType<T>>>(): ConditionalFactoryReturnFn<ExtractType<T>> { 
    return ((p: ExtractType<T>): BaseType<ExtractType<T>> => {
        return { data: p };
    }) as unknown as ConditionalFactoryReturnFn<ExtractType<T>>;
}
// specify a concrete type derived from BaseType, with different generic types except boolean
// number, string, string[], { readonly id: string } etc.
type SpecificType = BaseType<{ readonly id: string }>;
const callbackWithTypes = factory<SpecificType>();
callbackWithTypes({  id: "string" });


// specify a concrete type derived from BaseType, with boolean
type SpecificTypeBoolean = BaseType<boolean>;
// the callback signature returned from the factory becomes a union type 
// and passing true | false tsc error
// Argument of type 'true' is not assignable to parameter of type 'never'.(2345)
const callbackWithBoolean = factory<SpecificTypeBoolean>();
callbackWithBoolean(true); // passing true or false is no possible

// expected signature
// callbackWithBoolean === FactoryReturnAFn<boolean>
// actual signature
// callbackWithBoolean ===  FactoryReturnAFn<false> | FactoryReturnAFn<true>

Expected behavior:
type defintion to be properly using boolean type FactoryReturnAFn<boolean>

Actual behavior:
type definition is union type FactoryReturnAFn<false> | FactoryReturnAFn<true> and blocks to call fun ction with true or false as parameter.

Playground Link: Example

Related Issues:
#33369

@RyanCavanaugh
Copy link
Member

See e.g. #36683

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Feb 27, 2020
@atothek1
Copy link
Author

atothek1 commented Feb 28, 2020

@RyanCavanaugh thanks for the answer after playing a bit around I have got it what to do, was for me not that obvious. I post the solution here.

interface BaseType<T = undefined> {
    readonly data: T;
}

// infered types
type ExtractType<T> = T extends BaseType<infer A> ? A : undefined;

// define type definitions for function signatures that the factory can return
type FactoryReturnAFn<T> = (p: T) => BaseType<T>;
type FactoryReturnBFn<T> = (p: T) => BaseType<number>;

// define a conditional type, FIX: [T]
type ConditionalFactoryReturnFn<T> = [T] extends undefined
    ? FactoryReturnBFn<T>
    : FactoryReturnAFn<T>;

function factory<T extends BaseType<ExtractType<T>>>(): ConditionalFactoryReturnFn<ExtractType<T>> { 
    return ((p: ExtractType<T>): BaseType<ExtractType<T>> => {
        return { data: p };
    }) as unknown as ConditionalFactoryReturnFn<ExtractType<T>>;
}

type SpecificType = BaseType<{ readonly id: string }>;
const callbackWithTypes = factory<SpecificType>();
callbackWithTypes({  id: "string" });

type SpecificTypeBoolean = BaseType<boolean>;
const callbackWithBoolean = factory<SpecificTypeBoolean>();
callbackWithBoolean(true); 

type MyUnion = "a" | true | 100 | string[];
type SpecificTypeUnion = BaseType<MyUnion>;
const callbackWithUnion = factory<SpecificTypeUnion>();
callbackWithUnion(true); 
callbackWithUnion("a"); 
callbackWithUnion(100); 
callbackWithUnion(["works"]); 
callbackWithUnion(false);

@typescript-bot
Copy link
Collaborator

This issue has been marked as a 'Duplicate' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

3 participants