Typescript: Specify whether generic is allowed to be infered as {}

Created on 20 May 2017  路  12Comments  路  Source: microsoft/TypeScript

I would like to specify that 'To' is not allowed to be inferred as '{}'.
Perhaps some compilation flag, --noInferObjectFromGeneric, or something else.

function convert<To, From>(from: From): To {
    return from as any as To; // doesn't really matter
}

var x = convert(1);  // To = {}
Declined Suggestion

All 12 comments

Are you suggesting that the generic be inferred as any? Essentially {} is equivalent to any anyways.

Default generics allow you to suggest something else to inferred:

function convert<To = string, From = string>(from: From): To {
    return from as any as To; // doesn't really matter
}

var x = convert(1);  // To = string

No, I mean to get a compiler error in such case.

To here is rather meaningless as a type parameter. there is no way for the code at run-time to observe what To is. so this is merely a type cast at compile time. The language already has support for this in the form of type assertions. I would recommend using that instead.

It is just a sample.
I want an option to disallow using such functions.

Can you shed some light on the scenario.

Only if all generic parameters can be deduced either manually or from parameters, the function is allowed to called.
Similar to way it is implemented in C#

i understand. can you share a code sample where you feel you need this?

Let's take this sample:

```ts
class Base { field: T; }
class Sub extends Base { constructor(public field: T) { super(); } }
function test, T2>(arr: A, t2: T2) {
const data: Array = [];
data.push(arr.field);
data.push(t2);
return { input: arr, data };
}

const res = test(new Sub(1), "a"); // T1 = {} , A = Sub, T2 = string
const first: number|string = res.data[0]; // error
````

Nothing is wrong here and type inference is according to the rules.
The only problem is that T1 inferred as {}/
For my understanding it happens since there is no type specified for T1 from parameters and TSC can choose anything it wants, so {} is good enough.

I want to have such functions to not compile and require writing explicit T1..

I don't want to talk about high-order kinds which solves the problem.
Maybe both samples are different issues.
The main part is that I don't want the implicitly deduced type {} just because there is no other option.

Here's a workaround, perhaps not to be taken too seriously (edited to work if T1 is set to a type variable in scope at the call site):

const DUMMY = Symbol();
interface Do_not_mess_with_this_type_parameter {
    [DUMMY]: never;
}
type IfDefinitelyNever<X, A, B, G extends Do_not_mess_with_this_type_parameter> =
    ("good" | G) extends {[P in keyof X]: "good"}[keyof X] ? B : ([X] extends [never] ? A : B);

class Base<T> { field: T; }
class Sub<T> extends Base<T> { constructor(public field: T) { super(); } }
interface T1_was_not_inferred_please_specify_it {
    [DUMMY]: never;
}
function test<T1 = never, A extends Base<T1> = Base<T1>, T2 = never,
    G extends Do_not_mess_with_this_type_parameter = never>(
    arr: A & IfDefinitelyNever<T1, T1_was_not_inferred_please_specify_it, {}, G>, t2: T2) {
    let arr_: A = arr;
    const data: Array<T1 | T2> = [];
    data.push(arr_.field);
    data.push(t2);
    return { input: arr_, data };
}

// error: Argument of type 'Sub<number>' is not assignable to parameter of type 'Sub<number> & T1_was_not_inferred_please_specify_it'.
const res = test(new Sub(1), "a");
const first: number|string = res.data[0];

I support the suggestion. I have another use case for it that I'll write up if I can think of a simple way to explain it.

Here's another example (thanks @mattmccutchen for the answer there!).

Setting the default to never or unknown is pretty much guaranteed to produce a downstream error. Uninferrable type parameters are a strong anti-pattern regardless; the example code in the OP simply should not be written.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

MartynasZilinskas picture MartynasZilinskas  路  3Comments

DanielRosenwasser picture DanielRosenwasser  路  3Comments

wmaurer picture wmaurer  路  3Comments

Antony-Jones picture Antony-Jones  路  3Comments

blendsdk picture blendsdk  路  3Comments