Typescript: Question: keys of a type which have a certain type

Created on 1 Sep 2017  ·  6Comments  ·  Source: microsoft/TypeScript

The type system is awesome, but I am struggling with this problem.

class View<TMode> {
    public sortBy(source: TModel[], property: keyof TModel): void {
    }
}

Is it possible to restrict the second parameter to those keys of TModel which type is string?

For example, if I have an object like this:

{
    "id": 1,
    "name": "hello",
}

Then the only allowed key is "name".

If not supported now,

  • does any workaround exists with advanced type arithmetic?
  • is it planned?

Thanks.

Question

Most helpful comment

TS 2.8

export type StringProps<T> = ({ [P in keyof T]: T[P] extends string ? P : never })[keyof T];

_nb: this one will also allow string[]_

All 6 comments

This is not a support forum.

Questions should be asked at StackOverflow or on Gitter.im.

Also, it seems to work as you would expect, unless I am missing something:

class View<T> {
    public sortBy(source: T[], property: keyof T): void {
    }
}

const v = new View<{ id: number; name: string; }>();

v.sortBy([], 'name');

Thank you for your reply. It's my bad, I used the type string which makes the question ambiguous. My question is more of a feature-related question, this is why I chose this channel. If you still see this is not the place for this question then feel free to close it.

It should work with "name", but "id" should not be allowed, because its type is number. So, if I have a type like { id: number, name: string, description: string }, is there any way to reference only "name" and "description" (based on their types, excluding "id") as a literal type?

As far as I know, it is not possible now, but a workaround might exists like in #18133 or it might be a proposal for future versions. It is also very similar to #13214.

Actually, as you point out by linking to #13214 that this is actually a duplicate of #12424 which is a feature being considered for the future. Of course researching these things _before_ you open an issue would be great.

There is a workaround but keep in mind that these workarounds are quite fragile. E.g. try to make description optional, or string | undefined.

type HasKey<T, Key extends string> = (
  { [K in keyof T]: 'true' } &
  { [key: string]: 'false' }
)[Key]

// if the type has a `charCodeAt` property we deem it a `string`
// obviously, this might not always be the case
type IsString<T> = HasKey<T, 'charCodeAt'>

type StringProps<T> = {
    [K in keyof T]: {
        'true': K,
        'false': never
    }[IsString<T[K]>]
}[keyof T]

class View<T> {
    public sortBy<K extends StringProps<T>>(_source: T[], _property: K): void {
    }
}

const v = new View<{
    id: number;
    name: string;
    description: string;
}>();

v.sortBy([], 'description');

TS 2.8

export type StringProps<T> = ({ [P in keyof T]: T[P] extends string ? P : never })[keyof T];

_nb: this one will also allow string[]_

Nice @VinceOPS

Any way to make it work on private methods?

class SomeClass {
  public prop;
  constructor() {}

  public someMethod() {}

  private somePrivateMethod() {}

  private other() {
    this.callWithThis('prop'); // proper warning, since it's not a Function
    this.callWithThis('someMethod'); // properly no warning, since it's a Function
    this.callWithThis('somePrivateMethod'); // warning since it's private
  }

  private callWithThis(classFnName: MethodProp) {
    const self = this;
    return () => { return self[classFnName].call(self); };
  }
}

type MethodProp = ({
  [T in keyof SomeClass]: SomeClass[T] extends Function ? T : never
})[keyof SomeClass]
Was this page helpful?
0 / 5 - 0 ratings

Related issues

fwanicka picture fwanicka  ·  3Comments

siddjain picture siddjain  ·  3Comments

blendsdk picture blendsdk  ·  3Comments

bgrieder picture bgrieder  ·  3Comments

seanzer picture seanzer  ·  3Comments