Skip to content

Question: keys of a type which have a certain type #18211

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Peter-Juhasz opened this issue Sep 1, 2017 · 6 comments
Closed

Question: keys of a type which have a certain type #18211

Peter-Juhasz opened this issue Sep 1, 2017 · 6 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@Peter-Juhasz
Copy link

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.

@kitsonk
Copy link
Contributor

kitsonk commented Sep 1, 2017

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');

@Peter-Juhasz
Copy link
Author

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.

@kitsonk
Copy link
Contributor

kitsonk commented Sep 1, 2017

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.

@gcnew
Copy link
Contributor

gcnew commented Sep 1, 2017

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');

@VinceOPS
Copy link

VinceOPS commented Apr 12, 2018

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[]

@jdforsythe
Copy link

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]

@RyanCavanaugh RyanCavanaugh added the Question An issue which isn't directly actionable in code label Jul 5, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

6 participants