Skip to content
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

Generic argument type inference regression in TS 3.4 #30074

Closed
ulrichb opened this issue Feb 24, 2019 · 3 comments
Closed

Generic argument type inference regression in TS 3.4 #30074

ulrichb opened this issue Feb 24, 2019 · 3 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@ulrichb
Copy link

ulrichb commented Feb 24, 2019

TypeScript Version: 3.4.0-dev.20190223

Search Terms: 3.4 inference generic readonly

Code

The following snippet emits an error in TS 3.4 whereas in TS 3.3 it compiles fine.

class ErrorResult {
    readonly isError = true;

    static combine(errorResults: ReadonlyArray<ErrorResult>): ErrorResult {
        return new ErrorResult(/* ... combine errorResults ... */);
    }
}

//

function extractAndCombineErrorResults<T, TErrorResult extends ErrorResult>(
    allResults: ReadonlyArray<T | TErrorResult>,
    combine: (details: ReadonlyArray<TErrorResult>) => TErrorResult,
): ReadonlyArray<T> | TErrorResult {

    return /* dummy result: */ [];
}

//

interface SomeResult {
    isSomeResult: true
}

const allResults: ReadonlyArray<SomeResult | ErrorResult> = [];

// Here the inference doesn't work anymore:
const result1: ReadonlyArray<SomeResult> | ErrorResult =
    extractAndCombineErrorResults(allResults, ErrorResult.combine);

// Explicit generic args work:
const result2: ReadonlyArray<SomeResult> | ErrorResult =
    extractAndCombineErrorResults<SomeResult, ErrorResult>(
        allResults, ErrorResult.combine);

Command line: npx tsc TypeScriptIssue.ts

Expected behavior:
No compile error.

Actual behavior:

TSC emits

TypeScriptIssue30074.ts:28:7 - error TS2322: Type 'ErrorResult | readonly (ErrorResult | SomeResult)[]' is not assignable to type 'ErrorResult | readonly SomeResult[]'.
  Type 'readonly (ErrorResult | SomeResult)[]' is not assignable to type 'ErrorResult | readonly SomeResult[]'.
    Type 'readonly (ErrorResult | SomeResult)[]' is not assignable to type 'readonly SomeResult[]'.
      Type 'ErrorResult | SomeResult' is not assignable to type 'SomeResult'.
        Property 'isSomeResult' is missing in type 'ErrorResult' but required in type 'SomeResult'.

28 const result1: ReadonlyArray<SomeResult> | ErrorResult =
         ~~~~~~~

Playground Link: (works in TS 3.3)

Possibly related: #29435

@ahejlsberg
Copy link
Member

This is working as intended and is an effect of #29847. In your example, we now end up making inferences from the allResults parameter which we previously wouldn't do. Therefore, in the result1 assignment we now infer SomeResult | ErrorResult for T, and we'll make that inference even if you omit the type annotation for result1 (where previously we'd then infer {} for T).

@ahejlsberg ahejlsberg added the Working as Intended The behavior described is the intended behavior; this is not a bug label Feb 25, 2019
@ulrichb
Copy link
Author

ulrichb commented Feb 26, 2019

Hi @ahejlsberg,

ah, now I see that in TS 3.3 the inference comes from the result assignment (without, it inferred ReadonlyArray<SomeResult | ErrorResult> | ErrorResult).

Many thanks for your reply!


I've now found an alternative solution to make inference work again (based on the second combine argument, which fixates TErrorResult) using Exclude<T, TErrorResult> for the result.

// ...

function extractAndCombineErrorResults<T, TErrorResult extends ErrorResult>(
    allResults: ReadonlyArray<T>,
    combine: (details: ReadonlyArray<TErrorResult>) => TErrorResult,
): ReadonlyArray<Exclude<T, TErrorResult>> | TErrorResult {

    return /* dummy result: */ [];
}

//

const allResults: ReadonlyArray<SomeResult | ErrorResult> = [];

const result: ReadonlyArray<SomeResult> | ErrorResult =
    extractAndCombineErrorResults(allResults, ErrorResult.combine);

@typescript-bot
Copy link
Collaborator

This issue has been marked 'Working as Intended' 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
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

3 participants