Búsqueda de sitios web

genéricos - ¿puede TypeScript restringir keyof a una lista de propiedades de un tipo particular?


Entonces, quiero escribir un método genérico. Por el bien de la pregunta, digamos que quiero concatenar 2 propiedades de cadena de 2 objetos diferentes del mismo tipo.

function concat<T>(target1: T, target2: T, key: keyof T): string {
  return target1[key] + target2[key];
}

Lo anterior no se compila porque el compilador no tiene ninguna indicación de que quiera restringir key a ser solo la lista de propiedades de tipo string.

¿Esto no está actualmente soportado en TypeScript?

Sé que puedo hacer comprobaciones en tiempo de ejecución, pero quiero comprobaciones en tiempo de compilación para este.

Respuesta #1

Quieres un genérico de tres parámetros aquí. Uno para cada entrada y uno para la clave.

type CommonKey<A, B> = keyof A & keyof B

function concat<A, B, K extends CommonKey<A, B>>(
  target1: A,
  target2: B,
  key: K
): string {
  return `${target1[key]}${target2[key]}`
}

Esto define la clave como clave de A y B, lo que significa que debe satisfacer ambos.

Para restringirlo aún más solo a valores de cadena, necesitas usar un tipo mapeado con una condición para probar su tipo.

// This type returns all keys that have a value of type string
type StringKeyOf<T> = {
    // for all keys in T
    [K in keyof T]:

        // if the value of this key is a string, keep it. Else, discard it
        T[K] extends string ? K : never

// Get the union type of the remaining values.
}[keyof T]

// Only allow common keys of both objects.
type CommonKey<A, B> = StringKeyOf<A> & StringKeyOf<B>

function concat<
    A,
    B,
    K extends CommonKey<A, B>
>(target1: A, target2: B, key: K): string {
    return `${target1[key]}${target2[key]}`
}

Ahora esto dará un error de tipo, porque age es un número.

concat(
    { name: 'a', age: 12 human: 'maybe?' },
    { name: 'b', age: 8, dog: true },
    'age' // Type error

)

Artículos relacionados