I have two interfaces with identical optional keys, but different values:
interface Obj1 {
a?: string
b?: string
c?: string
}
interface Obj2 {
a?: boolean
b?: boolean
c?: boolean
}
Obj1 is served as a function argument, the other, Obj2, is the return of that function. I want the return type to identify only the given keys on Obj1. So if Obj1 contained only a
and b
then Obj2 will contain only a
and b
as well.
I tried with the approach bellow, but I get a ts error Type 'Property' cannot be used to index type 'ValueType'
type Obj1KeysWithObj2Values<KeyType extends Obj1, ValueType extends Obj2> = {
[Property in keyof KeyType]: ValueType[Property]
}
CodePudding user response:
Consider using Pick<Type, Keys> which is a part of standard library:
interface Obj1 {
a?: string
b?: string
c?: string
}
interface Obj2 {
a?: boolean
b?: boolean
c?: boolean
}
type Result = Pick<Obj2, keyof Obj1>
First argument represents source object, second argument represents a union of keys which should be picked
CodePudding user response:
This answer is completely generic
and it works.
I just want to share something similar which I did to convert Date
to a custom DateTimeFormatParts
formatted date using Intl
, except it's partially generic.
It worked like this:
// Accepts T which has any or all Intl.DateTimeFormatOptions properties
export type DateFormatPartsRecord<T extends Intl.DateTimeFormatOptions = Intl.DateTimeFormatOptions> = {
// modify keys to match props with result props
[P in keyof T as P extends "fractionalSecondDigits"
? "fractionalSecond"
: P extends "hour12"
? "dayPeriod"
: P]?: string;
};
// argument type for my caller function
export type DateIntlOptions<T extends Intl.DateTimeFormatOptions = Intl.DateTimeFormatOptions> = {
locale?: string | string[];
options?: T;
};
// the function which converts formatted intl array parts to my custom object type.
export function ToDateFormatParts<T extends Intl.DateTimeFormatOptions = Intl.DateTimeFormatOptions>(
val: number | string | Date,
intl?: DateIntlOptions<T>
): DateFormatPartsRecord<T> {
const toFormat = new Date(val);
const reduced: DateFormatPartsRecord<T> = {};
Intl.DateTimeFormat(intl?.locale, intl?.options ?? { day: "2-digit", month: "short", year: "numeric" })
.formatToParts(toFormat)
.forEach(({ type, value }) => {
if (type === "literal") return;
reduced[type] = value;
});
return reduced;
}
Applying this to your case, you can create a custom type based on the original:
// instead of obj2 interface:
type Obj2FromObj1<T extends Obj1> = {
[K extends keyof T]: boolean;
} // or a shorter version: Record<keyof T, boolean>
So now you can create a function to return only what props defined in the obj1:
function Obj2ValuesFromObj1<T extends Obj1>(args: T): Obj2FromObj1<T> {
// your handling here.
}