Adam Simon
Adam Simon

Reputation: 2970

TypeScript cannot infer constrained type in generic method?

I'm quite a newbie to TypeScript with a strong C# background.

I wonder what's the exact reasons that the type inference doesn't seem to work in the following situation in TypeScript but does in C#:

TypeScript:

interface IResult { }

interface IRequest<TResult extends IResult> { }

interface ISomeResult extends IResult {
    prop: string;
}

interface ISomeRequest extends IRequest<ISomeResult> { }

 function Process<TResult extends IResult>(request: IRequest<TResult>): TResult {
    throw "Not implemented";
}

let request: ISomeRequest = {};
let result = Process(request);
// Error: Property 'prop' does not exist on type '{}'.
console.log(result.prop);

C#

interface IResult { }

interface IRequest<TResult> where TResult : IResult { }

interface ISomeResult : IResult
{
    string Prop { get; set; }
}

interface ISomeRequest : IRequest<ISomeResult> { }

static TResult Process<TResult>(IRequest<TResult> request) where TResult : IResult
{
    throw new NotImplementedException();
}

static void Main()
{
    ISomeRequest request = default;
    var result = Process(request);
    // OK
    Console.WriteLine(result.Prop);
}

Is this an issue of the TS compiler's type inference algorithm (maybe it's not there yet?) or is there some fundamental reason which I am missing and makes this impossible in TS?

Upvotes: 1

Views: 425

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249536

Typescript has a structural type system (this might seem a bit weird when coming from C#, I know I had the same issue 😊). This means that is you have an unused type parameter, it will be ignored by the compiler as it does no really matter for type compatibility. Add any member that uses TResult to IRequest and it works as you expect it to:

interface IResult { }

interface IRequest<TResult extends IResult> { 
    _r?: undefined | TResult
}

interface ISomeResult extends IResult {
    prop: string;
}

interface ISomeRequest extends IRequest<ISomeResult> { }

function Process<TResult extends IResult>(request: IRequest<TResult>): TResult {
    throw "Not implemented";
}

let request: ISomeRequest = { };
let result = Process(request);
console.log(result.prop);

FAQ explaining some o this.

Upvotes: 1

Related Questions