Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get Type of Array in Typescript generics

Tags:

typescript

I have a method like this:

public select(fieldName: keyof TType)

Where TType could be an array type. In case of an array type, fieldName will correctly offer me all the propertynames of type Array.

In case I call this method with a type of User[] I want to get to the properties of User instead of the properties of Array.

Is there any way of how this could be done?

Extra question: Is there any way to restrict TType to be of an array type?

like image 815
Andreas Avatar asked Dec 14 '18 15:12

Andreas


People also ask

What is the type of an array in TypeScript?

In typescript, an array is a data type that can store multiple values of different data types sequentially. Similar to JavaScript, Typescript supports array declaration and there are multiple ways to do it. Declaring and Initializing Arrays: We can either use var or let for declaring an array.

How do I use generic types in TypeScript?

TypeScript generic type They can be used to identify that specific called function as a type. Making the function itself unaware of which type it's working with. To identify a generic type, you must prefix the function with <Type> where Type is the generic variable. Note: We often use T for generic types.

Can TypeScript array have different types?

An array in TypeScript can contain elements of different data types using a generic array type syntax, as shown below. let values: (string | number)[] = ['Apple', 2, 'Orange', 3, 4, 'Banana']; // or let values: Array<string | number> = ['Apple', 2, 'Orange', 3, 4, 'Banana'];

Should I use [] or array in TypeScript?

There is no difference at all. Type[] is the shorthand syntax for an array of Type . Array<Type> is the generic syntax. They are completely equivalent.


1 Answers

You can definitely make a conditional type function that unwraps an array type up to one level deep, and then use keyof on the result of that. For example:

// unwrap up to one level
type Unarray<T> = T extends Array<infer U> ? U : T;

// your class maybe
declare class Thingy<T> {
  constructor(t: T);
  public select(fieldName: keyof Unarray<T>): void;
}
// your interface maybe
interface User {
  name: string,
  age: number
}

declare const u1: User;
declare const u2: User;
const x = new Thingy(u1);
x.select("name"); // okay
const y = new Thingy([u1, u2]);
y.select("age"); // okay
y.select("push"); // error

That should work as you want, for the typings, I think. Obviously you also need to have an implementation which works (and note that conditional types in implementations usually require some type assertions or overloads to make the compiler happy... but you seem to be asking about the typings, not the implementation).


As for your extra question, yes, you can restrict T to just array types, as follows:

// your class maybe
declare class Thingy<T extends Array<any>> {
  constructor(t: T);
  public select(fieldName: keyof (T[number])): void;
}
// your interface maybe
interface User {
  name: string,
  age: number
}

declare const u1: User;
declare const u2: User;
const x = new Thingy(u1); // error
const y = new Thingy([u1, u2]);
y.select("age"); // okay

Note that I did away with the conditional types altogether here because it's more straightforward.


Hope that helps; good luck!

like image 176
jcalz Avatar answered Oct 19 '22 14:10

jcalz