Búsqueda de sitios web

typescript - ¿Cómo inferir el tipo de retorno a partir del tipo de parámetro en una función genérica?


Aquí hay algo de código con tipo condicional

class A {
    public a: number;
}

class B {
    public b: number;
}

type DataType = "a" | "b";

type TData<T extends DataType> =
    T extends "a" ? A :
    T extends "b" ? B :
    never;

Ahora quiero usar un tipo condicional como un enlace desde el parámetro de la función hasta su tipo de retorno. Intenté lograr esto de diferentes maneras sin resultado:

function GetData<T extends DataType>(dataType: T): TData<T> {
    if (dataType == "a")
        return new A();
    else if (dataType == "b")
        return new B();
}

¿Cuál es la sintaxis correcta? ¿Es posible con TypeScript 2.8?

Actualizar

Ya hay un problema abierto en GitHub que cubre mi ejemplo. Así que la respuesta actual es "No, pero podría ser posible en el futuro".

Respuesta #1

Puedes usar sobrecargas de funciones aquí:

function GetData(dataType: "a"): A;
function GetData(dataType: "b"): B;
function GetData(dataType: DataType): A | B {
    if (dataType === "a")
        return new A();
    else if (dataType === "b")
        return new B();
}

const f = GetData('a');  // Inferred A
const g = GetData('b');  // Inferred B

Respuesta #2

El operador ternario realmente no se lleva bien con los tipos genéricos:

type TriviallyA<T extends DataType> = 
      T extends any ? A : A;
function GetData<T extends 'a' = 'a'>(dataType: T): TriviallyA<T> {
    return new A(); // Error: Type 'A' is not assignable to type 'TriviallyA<T>'
}

Sin embargo, los genéricos funcionan bien con la búsqueda de atributos, por lo que puedes definir una interfaz para mapear cadenas a tipos particulares, luego puedes usar keyof y la búsqueda de atributos para actuar como TData:

interface DataTypeMapping {
    a: A;
    b: B;
}
type DataType = keyof DataTypeMapping;
type TData<T extends DataType> = DataTypeMapping[T];

function GetData<T extends DataType>(dataType: T): TData<T> {
    // now expected return type is A | B so this is valid!
    if (dataType === 'a') {
        return new A(); 
    } else if (dataType === 'b') {
        return new B();
    }
}

Artículos relacionados