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

Suggestion: deep version of StringKeyOf #432

Closed
clemclx opened this issue Aug 12, 2022 · 17 comments
Closed

Suggestion: deep version of StringKeyOf #432

clemclx opened this issue Aug 12, 2022 · 17 comments
Labels
component:paths enhancement New feature or request help wanted Extra attention is needed

Comments

@clemclx
Copy link
Contributor

clemclx commented Aug 12, 2022

Hey,

It could be great to have a deep version of the utility type StringKeyOf that would extracts nested keys with dot-notation.
Pretty useful when working with libraries using dot-notation to access values or to manipulate types.

Example

interface Foo {
  a: string;
  b: number;
  c: {
    nestedA: string;
  }
}

type Bar = StringKeyOfDeep<Foo>;
//   ^?  'a' | 'b' | 'c' | 'c.nestedA'

Upvote & Fund

  • We're using Polar.sh so you can upvote and help fund this issue.
  • The funding will be given to active contributors.
  • Thank you in advance for helping prioritize & fund our backlog.
Fund with Polar
@sindresorhus
Copy link
Owner

Pretty useful when working with libraries using dot-notation to access values or to manipulate types.

How is a union of all key paths useful for this?

@sindresorhus
Copy link
Owner

We already have a Get type to deal with key paths (dot-notation), and soon also a SetProp type: #409

@clemclx
Copy link
Contributor Author

clemclx commented Aug 15, 2022

Hey,

Get type does not return a union of all possible key paths.

A simple example :

I have a generic type with a prop (key here) representing an accessor in the provided type T (a key path)

interface ColumnDefinition<T> {
  id: string;
  label: string;
  key: StringKeyOf<T> // or keyof T
}

I have an interface representing my object, with nested properties

interface MyObject {
  _id: string;
  name: string;
  customData: {
    type: string;
    enabled: boolean;
  };
}

Then, when using my generic type

type MyObjectColumnDefinition = ColumnDefinition<MyObject>;
// here, key will be : '_id' | 'name' | 'customData'
// with a deep version it could have been : '_id' | 'name' | 'customData' | 'customData.type' | 'customData.type.enabled' adding deepProperties to union

I don't think the Get type you reference would allow it.

@dninomiya
Copy link

Additionally, it would be nice to have features like the Path type in React Hook Form. 🙏

It also supports arrays. (It may deviate from StringKeyOfDeep, but...)

type Post = {
  id: string;
  title: string;
  comments: {
    id: string;
    body: string;
  }[]
}

type fieldType = Path<Post>;

// type fieldType = "id" | "title" | "comments" | `comments.${number}` | `comments.${number}.id` | `comments.${number}.body`

Thanks for maintaining a great library!

@buschtoens
Copy link
Contributor

A leaves-only version of this would also be really useful:

type Post = {
  id: string;
  title: string;
  comments: {
    id: string;
    body: string;
  }[]
}

type fieldType = Path<Post, { onlyLeaves: true }>;

// type fieldType = "id" | "title" | `comments.${number}.id` | `comments.${number}.body`

@bombillazo
Copy link

I was hoping StringKeyOf was deeply nested but sad it wasnt.

However, another library I am using, react-hook-form, has a similar concept util type call Path:

/**
 * Helper type for recursively constructing paths through a type.
 * See {@link Path}
 */
declare type PathImpl<K extends string | number, V> = V extends Primitive | BrowserNativeObject ? `${K}` : `${K}` | `${K}.${Path<V>}`;
/**
 * Type which eagerly collects all paths through a type
 * @typeParam T - type which should be introspected
 * @example
 * ```
 * Path<{foo: {bar: string}}> = 'foo' | 'foo.bar'
 * ```
 */
export declare type Path<T> = T extends ReadonlyArray<infer V> ? IsTuple<T> extends true ? {
    [K in TupleKeys<T>]-?: PathImpl<K & string, T[K]>;
}[TupleKeys<T>] : PathImpl<ArrayKey, V> : {
    [K in keyof T]-?: PathImpl<K & string, T[K]>;
}[keyof T];

@sindresorhus
Copy link
Owner

Duplicate of #213

@sindresorhus sindresorhus marked this as a duplicate of #213 Nov 20, 2022
@sindresorhus
Copy link
Owner

This is accepted. The type should be named Paths.

@sindresorhus
Copy link
Owner

I agree it should include a onlyLeaves: true option as mentioned in #432 (comment).

@sindresorhus
Copy link
Owner

See the initial attempt at adding this here: #300

@sindresorhus
Copy link
Owner

Don't forget to add StringKeyOfDeep as an alternative name here: https://github.com/sindresorhus/type-fest#alternative-type-names

@sindresorhus
Copy link
Owner

@sindresorhus
Copy link
Owner

Also some inspiration in #213

@voxpelli
Copy link
Collaborator

I think this was completed in #741

@stefanprobst
Copy link

@voxpelli i think one thing still open is a "leaves only" option, see #432 (comment)

@voxpelli
Copy link
Collaborator

@stefanprobst Lets keep track of that in #860, easier to pinpoint then. Does that sound good?

@stefanprobst
Copy link

Lets keep track of that in #860, easier to pinpoint then. Does that sound good?

perfect, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component:paths enhancement New feature or request help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

7 participants