typescript - ¿cómo puedes expresar "keyof T" para que sea un subconjunto de String?
Los tipos de Firebase tienen una interfaz IQuery que tiene el método equalTo() tipado como:
public equalTo(value: number | string | boolean | null, name?: string): IQuery;
Tengo una biblioteca de simulación que está intentando imitar la interfaz de API de lo que Firebase está exponiendo, pero en el caso del parámetro name, la tipificación general de "string" se puede hacer más específica:
public equalTo(value: number | string | boolean | null, key?: keyof T): IQuery<T>
Al declarar keyof T obtenemos algunas comprobaciones estáticas muy útiles que odiaría perder, y de hecho hasta TypeScript 2.8.3 parecía que podía hacer esto, pero al pasar a 2.9.x+ de TypeScript ahora obtengo este error:
El tipo 'string' no se puede asignar al tipo 'keyof T'.
Para un poco más de contexto, la definición de la clase en mi biblioteca de simulación es:
export default class Query<T = any> implements IQuery<T> { ... }
Donde el tipo genérico <T> es un indicador del tipo de la estructura de datos que la consulta devolverá.
Además, vale la pena señalar que T, siempre estará estructurado como un diccionario/hash con claves de tipo cadena y, por lo tanto, keyof T será un subconjunto de valores de cadena, pero en mi tipado no estoy dejando esto claro, así que entiendo de dónde viene el problema de TypeScript... simplemente no sé cómo expresar que keyof T debe ser una cadena.
Respuesta #1
Esto está relacionado con un cambio en la forma en que keyof funciona en la 2.9, hasta la 2.9 keyof solo devolvía claves string, a partir de la 2.9 devolverá claves number y symbol. Esta es la referencia para esto
Para aceptar solo claves string, puedes usar Extract<keyof T, string> como el tipo para el parámetro key, o si quieres volver al comportamiento anterior puedes usar la bandera del compilador --keyofStringsOnly
public equalTo(value: number | string | boolean | null, key?: Extract<keyof T, string>): IQuery<T>
Respuesta #2
También puedes requerir que las claves en tu tipo sean solo de texto:
key?: keyof T & string
