Skip to content
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

Support RequiredKeys and OptionalKeys #53

Closed
malash opened this issue Jan 11, 2019 · 16 comments · Fixed by #79
Closed

Support RequiredKeys and OptionalKeys #53

malash opened this issue Jan 11, 2019 · 16 comments · Fixed by #79

Comments

@malash
Copy link
Contributor

malash commented Jan 11, 2019

Demo:

type Obj = {
  a: number;
  d: number | undefined
  b?: string;
  c?: boolean;
  e?: number | undefined
};
type A = RequiredKeys<Obj>; // "a" | "d"
type B = OptionalKeys<Obj>; // "b" | "c" | "e"

https://stackoverflow.com/questions/53809467/typescript-how-get-optional-keys-of-type

EDIT1: Added edge cases

@piotrwitek
Copy link
Owner

Looks useful, accepting PR's!

Requirements:
Add 2 new types to the public API based on the OP demo usage.

  • RequiredKeys
  • OptionalKeys

If you want to introduce some helper types please keep them private

@levenleven
Copy link
Contributor

I'd like to add NullableKeys as well.
With strictNullChecks enabled can be implemented as

type PickKeys<T extends object, TValue extends null | undefined> = NonNullable<
  { [K in keyof T]: TValue extends T[K] ? K : never }[keyof T]
>

type NullableKeys<T extends object> = PickKeys<T, null>
type OptionalKeys<T extends object> = PickKeys<T, undefined>
type RequiredKeys<T extends object> = Exclude<keyof T, OptionalKeys<T>>

Playground (works only with strictNullChecks)

@piotrwitek
Copy link
Owner

piotrwitek commented Jan 14, 2019

@levenleven
Although they might seem useful API surface will expand making it complex and hard to learn.
Now I think that maybe it would be better to add more generic types to allow consumer for better composability making it easier to master and allowing for even better flexibility and extendability:

type ExtractKeys<T extends object, TKeyValue extends any> = NonNullable<
  { [K in keyof T]: TKeyValue extends T[K] ? K : never }[keyof T]
>;
type ExcludeKeys<T extends object, TKeyValue extends any> = Exclude<keyof T, ExtractKeys<T, TKeyValue>>;

type Foo = {
  str: string;
  num: number;
  date: Date | null;
  bool?: boolean;
};

type NullableProps = Pick<Foo, ExtractKeys<Foo, null>>;
type OptionalProps = Pick<Foo, ExtractKeys<Foo, undefined>>;
type RequiredProps = Pick<Foo, ExcludeKeys<Foo, undefined>>;

What do you think?

EDIT: We might add both if that make sense

@levenleven
Copy link
Contributor

levenleven commented Jan 15, 2019

I think ExtractKeys/ExcludeKeys are good for flexibility, but I'd also include shortcuts to RequiredKeys and others.

Now regarding names:

  1. Key helpers - maybe it would be clearer if we'd call them ExtractKeysOfType or KeysAssignableTo?
  2. Property set helpers (not sure if needed or in scope of this proposal) - PickNullable, PickOptional etc. (to align with Pick naming convention)

@piotrwitek
Copy link
Owner

piotrwitek commented Jan 15, 2019

I agree we could greatly benefit from both general helpers and shortcuts.

New API Proposal for extending Pick/Omit and Object-Keys Helpers

...

Moved to #59

@levenleven
Copy link
Contributor

I'm for OptionalKeys. Also I don't think there's inconsistency with Partial, because props can optional but can't be partial, and the other way around - object can be partial but not optional

@piotrwitek
Copy link
Owner

piotrwitek commented Jan 15, 2019

I understand your semantics argument, but in reality Partial equals Optional. In general, I think Optional would be a better name and for that reason, the best would be to use OptionalKeys and create an alias (Optional) for Partial as well to keep the API consistent.

@malash
Copy link
Contributor Author

malash commented Jan 16, 2019

I find a bug in the StackOverflow version.

type KeysOfType<T, U> = { [K in keyof T]: T[K] extends U ? K : never }[keyof T];
type RequiredKeys<T> = Exclude<KeysOfType<T, Exclude<T[keyof T], undefined>>, undefined>;
type OptionalKeys<T> = Exclude<keyof T, RequiredKeys<T>>;

type Obj = {
  a: any;
  b?: string;
  c?: boolean;
};
type A = RequiredKeys<Obj>; // "a" | "b" | "c"
type B = OptionalKeys<Obj>; // never

@malash
Copy link
Contributor Author

malash commented Jan 16, 2019

And another issue is that how to handle { d: number | undefined } ?

Should its RequiredKeys be 'd' or never ?

@malash
Copy link
Contributor Author

malash commented Jan 16, 2019

@piotrwitek I think it could be better if we create a new issue for the new API proposal ?

@levenleven
Copy link
Contributor

@malash see above working version.

Regarding number | undefined - I think this should be treated as optional, because what ? essentially does - is adding undefined to the type (with strictNullChecks of course)

@malash
Copy link
Contributor Author

malash commented Jan 16, 2019

type A = Required<{ a: number | undefined }>;
type B = Required<{ a?: number }>;

A is still { a: number | undefined }

B is { a: number }

@levenleven
Copy link
Contributor

@malash interesting, seems you're right. We'll need to tweak Required/Optional resolution

@eps1lon
Copy link

eps1lon commented Jan 16, 2019

Depending on the implementation (Object.prototype.hasOwnProperty can make a difference here) those are not the same thing at run-time. The first one still requires explicitly setting a to undefined while you can do this implicitly with the second one by omitting a. You could argue that Required is working as intended.

@levenleven
Copy link
Contributor

levenleven commented Jan 16, 2019

Yeah, now it makes much more sense to tackle RequiredKey/OptionalKeys separately from all the other helpers proposed here. All the others are still perfectly valid

@eps1lon
Copy link

eps1lon commented Jan 16, 2019

Right because I would argue that RequiredKeys should include number | undefined. After all the key is required. Just not the value.

piotrwitek added a commit that referenced this issue Apr 28, 2019
- Added RequiredKeys and OptionalKeys #53
- Added OmitByValue and PickByValue to the public API #50 
- Added OmitByValueExact and PickByValueExact #59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants