简体   繁体   中英

Can a generic identity function infer tuple types?

I have a function that effectively returns its argument, currently typed as:

function h<T>(x: T): T {
  // Actually do some more things, then:
  return x;
}

When I try this typing in the playground, the generic type is inferred as I expect for the following:

h(123); // T: 123
h('stringval'); // T: 'stringval'

But when I pass a tuple, it gives me a union array type:

h(['a', 2]); // T: (string|number)[]

Is there a way to write h(x) s type so that tuple arguments are inferred, or do I need to use a helper like:

function tuple<T extends any[]>(...args: T): T {
    return args;
}

h(tuple('a', 2));

I would prefer not to force people to manually tag their tuple types, instead to infer them when possible.

But when I pass a tuple, it gives me a union array type:

 h(['a', 2]); // T: (string|number)[]

You write "when I pass a tuple", but you are not passing a tuple. You are passing an array.

Is there a way to write h(x) s type so that tuple arguments are inferred

TypeScript will never infer a tuple type from an array literal. Tuples are a particular way of using arrays. TypeScript cannot read your mind, it cannot possibly know how you intend to use that array, so tuples always need to be explicitly typed.

The only way for your identity function to return a tuple is by passing it a tuple. When you are passing it an array literal, you are not passing a tuple, you are passing an array.

If you actually pass a tuple, your identity function works just fine:

const thisIsAnArray                  = ['a', 2]; // inferred as (string | number)[]
const thisIsATuple: [string, number] = ['a', 2];
const thisIsAlsoATuple               = ['a', 2] as const;

h(thisIsAnArray);    // T: (string | number)[]
h(thisIsATuple);     // T: [string, number]
h(thisIsAlsaATuple); // T: readonly ['a', 2]

Note that nothing of this has anything to do with your function. Your function is working perfectly fine: it is returning exactly what is passed in, and its return type is exactly the type of the parameter.

In your test, you were never passing in a tuple in the first place, and thus you weren't getting a tuple out. You were getting out exactly what you were passing in: an array.

This is a solution that worked for me:

function h<T extends any[] | []>(x: T): T;

h([1, 2, "hello"]); // => [number, number, string]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM