Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how can you express "keyof T" to be a subset of String?

Tags:

typescript

The Firebase typings have an interface IQuery which has the equalTo() method typed as:

public equalTo(value: number | string | boolean | null, name?: string): IQuery;

I have a mocking library which is trying to mimic the API surface of what Firebase is exposing but in the case of the name parameter the general typing of "string" can be made more specific:

 public equalTo(value: number | string | boolean | null, key?: keyof T): IQuery<T>

By stating keyof T we get some very useful static checking that I'd hate to lose out on and in fact up to TypeScript 2.8.3 I seemed to be able to do this but when moving to 2.9.x+ of Typescript I now get this error:

Type 'string' is not assignable to type 'keyof T'.

For a bit more context, the class definition in my mocking library is:

 export default class Query<T = any> implements IQuery<T> { ... }

Where the <T> generic type is an indicator of type of the data structure that the query will return.

Further it is worth noting that T, will always be shaped as a dictionary/hash with string keys and therefore keyof T will subset of string values but in my typing I'm not making this clear so I get where Typescript is taking issue ... I just don't know how to express that keyof T must be a string.

like image 700
ken Avatar asked Jun 02 '18 05:06

ken


People also ask

What does Keyof Typeof do?

keyof is a keyword in TypeScript which is used to extract the key type from an object type.

What does Keyof return?

keyof T returns a union of string literal types. The extends keyword is used to apply constraints to K, so that K is one of the string literal types only. extends means “is assignable” instead of “inherits”' K extends keyof T means that any value of type K can be assigned to the string literal union types.


2 Answers

This is related to a change in the way keyof works in 2.9, until 2.9 keyof only returned string keys, from 2.9 onward it will return number and symbol keys. This is the reference for this

To only accept string keys you can use Extract<keyof T, string> as the type for the key parameter, or if you want to revert to the old behavior you can use the --keyofStringsOnly compiler flag

public equalTo(value: number | string | boolean | null, key?: Extract<keyof T, string>): IQuery<T>
like image 168
Titian Cernicova-Dragomir Avatar answered Sep 28 '22 01:09

Titian Cernicova-Dragomir


You can also require keys in your type to be string-only:

key?: keyof T & string
like image 27
madox2 Avatar answered Sep 28 '22 02:09

madox2