is there a way to get all required properties of a typescript interface or an object.
something like Object.getOwnPropertyDescriptors(myObject) or keyof T but with the information property is required/optional
is there a way to get all required properties of a typescript interface or an object.
something like Object.getOwnPropertyDescriptors(myObject) or keyof T but with the information property is required/optional
At runtime this is not possible, because requiredness/optionality of a property only exists in the TypeScript type system, which has been erased by the time the code actually runs. You can add your own runtime information via decorators or the like, but for that you need to modify the actual code that generates the classes and objects. So getting an array of required property names given an object or constructor is not possible.
At design time it is possible to extract the required/optional keys of a type, as a subtype of keyof T. The solution relies on conditional types and the fact that the empty object type {} is considered assignable to a weak type (a type with no required properties). Like this:
type RequiredKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? never : K }[keyof T];
type OptionalKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? K : never }[keyof T];
And an example usage:
interface SomeType {
required: string;
optional?: number;
requiredButPossiblyUndefined: boolean | undefined;
}
type SomeTypeRequiredKeys = RequiredKeys<SomeType>;
// type SomeTypeRequiredKeys = "required" | "requiredButPossiblyUndefined"
type SomeTypeOptionalKeys = OptionalKeys<SomeType>;
// type SomeTypeOptionalKeys = "optional"
That doesn't play nicely with types with index signatures:
interface SomeType {
required: string;
optional?: number;
requiredButPossiblyUndefined: boolean | undefined;
[k: string]: unknown; // index signature
}
type SomeTypeRequiredKeys = RequiredKeys<SomeType>;
// type SomeTypeRequiredKeys = never
type SomeTypeOptionalKeys = OptionalKeys<SomeType>;
// type SomeTypeOptionalKeys = string
Not sure if your use case cares about indexable types or not. If so, there is a more complex solution which handles that by first extracting known literal keys and then checking for required/optional:
(EDIT: the following was updated to work around a breaking change in TS4.3, see ms/TS#44143)
type RequiredLiteralKeys<T> = keyof { [K in keyof T as string extends K ? never : number extends K ? never :
{} extends Pick<T, K> ? never : K]: 0 }
type OptionalLiteralKeys<T> = keyof { [K in keyof T as string extends K ? never : number extends K ? never :
{} extends Pick<T, K> ? K : never]: 0 }
type IndexKeys<T> = string extends keyof T ? string : number extends keyof T ? number : never;
which results in:
type SomeTypeRequiredKeys = RequiredLiteralKeys<SomeType>;
// type SomeTypeRequiredKeys = "required" | "requiredButPossiblyUndefined"
type SomeTypeOptionalKeys = OptionalLiteralKeys<SomeType>;
// type SomeTypeOptionalKeys = "optional"
type SomeTypeIndexKeys = IndexKeys<SomeType>;
// type SomeTypeIndexKeys = string
if anyone in 2022 is still looking for an answer, this is what worked for me:
export type GetOptionalKeys<T> = {[K in keyof T as (undefined extends T[K] ? K : never)]: T[K]}
export type GetRequiredKeys<T> = {[K in keyof T as (undefined extends T[K] ? never : K)]: T[K]}
Resource: https://www.typescriptlang.org/docs/handbook/2/mapped-types.html#key-remapping-via-as
Here is my solution:
type _OptionalKeys<A extends object, B extends object> = {
[K in KnownKeys<A> & KnownKeys<B>]: Pick<A, K> extends Pick<B, K> ? never : K
};
/**
* OptionalKeys grabs the keys which are optional from a type `T`.
* For example, `{ a: string; b: string | undefined; c?: string }` => `'c'`.
*/
export type OptionalKeys<T extends object> = _OptionalKeys<
T,
Required<T>
>[KnownKeys<T>];
Basically, we're checking for OptionalKeys so that we can then extract the RequiredKeys.
/**
* RequiredKeys grabs the keys which are required from a type `T`.
* For example, `{ a: string; b: string | undefined; c?: string }` => `'b' | 'c'`.
*/
export type RequiredKeys<T extends object> = Exclude<
KnownKeys<T>,
OptionalKeys<T>
>;
And, what is KnownKeys?
/**
* Extracts the keys of a union type
*/
// tslint:disable-next-line:no-any
export type KeysOfUnion<T> = T extends any ? keyof T : never;
/**
* Extracts the known keys from an object – regardless of whether it has an
* index signature.
*/
export type KnownKeys<T extends object> = {
[K in keyof T]: string extends K ? never : number extends K ? never : K
} extends { [_ in keyof T]: infer U }
? {} extends U
? never
: U
: never;
Of course, you could invert this approach, so you'd find RequiredKeys first, and then find OptionalKeys by changing around the Pick<A, K> extends Pick<B, K> to Pick<B, K> extends Pick<A, K> in _OptionalKeys making it _RequiredKeys.
So this solution (I created it for my rbx package) works on union types as well as singleton types.
This worked for me
type RequiredProps<T> = {
[K in keyof T as string extends K ? never : number extends K ? never : {} extends Pick<T, K> ? never : K]: T[K]
}
interface User {
id: string;
name: string;
age?: string;
height?: number;
interest: string[];
}
type A = RequiredProps<User>