-
-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Open
Description
7258 - Object Key Paths
Lodash introduced a lot of pretty bad concepts, let's all agree on that much, and this challenge is the fallout from one of them. It's something we've seen before in the challenges to deal with the string interpolation syntax for object indexing.. but it's still just as bad as it was before. At least this time there are some fun/helpful refactors to do.
🎥 Video Explanation
🔢 Code
// ============= Test Cases =============
import type { Equal, Expect, ExpectFalse } from './test-utils'
type A1 = ObjectKeyPaths<{
name: string;
age: number;
}>;
type B1 = 'name' | 'age';
type C1 = Expect<Equal<A1, B1>>;
type A2 = ObjectKeyPaths<{
refCount: number;
person: {
name: string;
age: number;
};
}>;
type B2 = 'refCount' | 'person' | 'person.name' | 'person.age';
type C2 = Expect<Equal<A2, B2>>;
type RefPaths = ObjectKeyPaths<{
count: 1,
person: {
name: 'cattchen',
age: 22,
books: ['book1', 'book2'],
pets: [
{
type: 'cat',
},
],
},
}>;
type ExpectExtends<T> =
T extends RefPaths
? true
: false;
type Cases = [
Expect<ExpectExtends<'count'>>,
Expect<ExpectExtends<'person'>>,
Expect<ExpectExtends<'person.name'>>,
Expect<ExpectExtends<'person.age'>>,
Expect<ExpectExtends<'person.books'>>,
Expect<ExpectExtends<'person.pets'>>,
Expect<ExpectExtends<'person.books.0'>>,
Expect<ExpectExtends<'person.books.1'>>,
Expect<ExpectExtends<'person.books[0]'>>,
Expect<ExpectExtends<'person.books.[0]'>>,
Expect<ExpectExtends<'person.pets.0.type'>>,
ExpectFalse<ExpectExtends<'notExist'>>,
ExpectFalse<ExpectExtends<'person.notExist'>>,
ExpectFalse<ExpectExtends<'person.name.'>>,
ExpectFalse<ExpectExtends<'.person.name'>>,
ExpectFalse<ExpectExtends<'person.pets.[0]type'>>,
];
// ============= Your Code Here =============
// @teamchong
// @humandetail
type AddPrefix<
P extends string,
Path extends string | number,
> =
[P] extends [never]
? `${Path}`
: Path extends number
? | `${P}.${Path}`
| `${P}[${Path}]`
| `${P}.[${Path}]`
: `${P}.${Path}`;
type ObjectKeyPaths<
T extends object,
P extends string = never,
> =
| P
| {
[K in keyof T & (string | number)]:
T[K] extends object
? ObjectKeyPaths<T[K], AddPrefix<P, K>>
: AddPrefix<P, K>
}[keyof T & (string | number)];
// @drylint
type ConcatPath<
P extends string | number,
K extends string | number,
> =
[P] extends [never]
? K
: | `${P}.${K}`
| (
[K] extends [number] // not necessary to []
? | `${P}[${K}]`
| `${P}.[${K}]`
: never
);
type ObjectKeyPaths<
T,
P extends string | number = never,
K extends keyof T = keyof T,
> =
T extends Record<string, unknown>[] // can be object[]
? | ConcatPath<P, number>
| ObjectKeyPaths<T[number], ConcatPath<P, number>>
: T extends unknown[]
? ConcatPath<P, number>
: T extends Record<string, unknown> // can be object
? K extends string | number
? | ConcatPath<P, K>
| ObjectKeyPaths<T[K], ConcatPath<P, K>>
: never
: never;➕ More Solutions
For more video solutions to other challenges: see the umbrella list! #21338
Metadata
Metadata
Assignees
Labels
No labels