简体   繁体   中英

TypeScript: how to extract the generic parameter from a type?

Say I have a type like React.ComponentClass<Props> and I want to reference the Props part but it is unnamed in my current context.

To illustrate:

const x = makeComponent(); // typeof x = React.ComponentClass<Something>

In this situation, I can use typeof x but that doesn't give me direct access to the Something type.

What I want is something like this:

type GetPropsType<C extends React.ComponentClass<P>> = P

So that I can extract Something with the type GetPropsType<typeof x>

Anything equivalent would be great.

You can use infer:

type TypeWithGeneric<T> = T[]
type extractGeneric<Type> = Type extends TypeWithGeneric<infer X> ? X : never

type extracted = extractGeneric<TypeWithGeneric<number>>
// extracted === number

Playground

You can use pattern matching for that:

namespace React {
    export class ComponentClass<T> {}
}

function doSomethingWithProps<T>(x : React.ComponentClass<T>) : T {
    return null as T;
}

class Something {}
let comp = new React.ComponentClass<Something>();
const instanceOfSomething = doSomethingWithProps(comp);

Thought I'd share a real world example to extract the R generic type for MatDialogRef<T, R = any> which is Angular material's dialog box.

I often have components like this, where the output 'model' is a simple interface that isn't quite worthy of its own named type.

export class DialogPleaseWaitComponent implements OnInit {

  constructor(@Inject(MAT_DIALOG_DATA) public data: DialogPleaseWaitModel,
              public dialogRef: MatDialogRef<DialogPleaseWaitModel, { timedOut: boolean }>) { 

  }

So I came up with:

extractMatDialogResponse<T>

I made it work in two 'modes', either taking the component type or the MatDialogRef itself. So I can put extractMatDialogResponse<DialogPleaseWaitComponent> and get back { timedOut: boolean } :-)

Yes - if T is the component type then it requires dialogRef to be the exact name of the property, and it does need to be public.

type extractMatDialogResponse<T = MatDialogRef<any, any> | { dialogRef: MatDialogRef<any, any> }> = 
     T extends MatDialogRef<any, infer R> ? R :                                                                                                          
     T extends { dialogRef: MatDialogRef<any, infer R> } ? R : never;

Also yes this is the same mechanism as used by Xiv, but demonstrates how to make a 'targeted' extractor for a specific use.

TypeScript generics are a compile time feature for type checking, and are not available as a JavaScript runtime feature after transpilation. You may want to take a look at this answer which has a pretty good example of how to construct your class so that you can access a representation of the generic from a class method.

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