这是indexloc提供的服务,不要输入任何密码
Skip to content

7258 - Object Key Paths (🎥 Video Explanation and Solution) #25079

@dimitropoulos

Description

@dimitropoulos

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

Release Date: 2023-04-30 19:00 UTC

Object Key Paths

🔢 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

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions