json-ptr
json-ptr copied to clipboard
Type safe JsonPointer.get
I made this type because I wanted a type safe way to pull properties from an object via a path, and I thought it might be useful to this library. I chose to make any properties not found in the object never, but you could use unknown to keep current functionality if you wanted. Just thought I'd share.
type TakeProp<T extends string> = T extends `${infer Prop}/${string}` ? Prop : never
type TakeRest<T extends string> = T extends `${string}/${infer Rest}` ? Rest : T
type PathExtract<Value, Path extends string> =
TakeProp<Path> extends never //if path type is not a path, get the prop from Value
? Path extends keyof Value
? Value[Path]
: never
: TakeProp<Path> extends keyof Value //check if prop is in value
? PathExtract<Value[TakeProp<Path>], TakeRest<Path>> //recurse
: never //failure
const num: number = PathExtract<{a: {b: number}}, "a/b">
This currently does not cover ~0 and ~1 but i think with a little more work that could be figured out.
Updated version that allows for number indexed types (like array)
type TakeProp<T extends string> = T extends `${infer Prop}/${string}` ? Prop : never
type TakeRest<T extends string> = T extends `${string}/${infer Rest}` ? Rest : T
export type PathExtract<Value, Path extends string> =
TakeProp<Path> extends never ? //if path type is not a path, get the prop from Value
Path extends `${number}` ? //if we are dealing with a number index
number extends keyof Value ? Value[number] : unknown
: Path extends keyof Value ? Value[Path] : unknown //otherwise just regular prop get
: TakeProp<Path> extends `${number}` ? //try to see if prop is a number
number extends keyof Value ?
PathExtract<Value[number], TakeRest<Path>>
: unknown
: TakeProp<Path> extends keyof Value ? //check if prop is in value
PathExtract<Value[TakeProp<Path>], TakeRest<Path>> //recurse
: unknown //failure
Finalized version. This one can handle decoding as well. Also cleaned up the types
//This is a big utility type to let us path walk a type with a string
export type SplitPath<T extends string> = T extends `${infer Prop}/${infer Rest}` ? [Prop, Rest] : [unknown, T]
//Deal with json pointer encoding
export type Decode<T extends string> =
T extends `${infer A}~0${infer B}` ? `${Decode<A>}~${Decode<B>}` :
T extends `${infer A}~1${infer B}` ? `${Decode<A>}/${Decode<B>}` :
T
export type ExtractProp<Value, Prop extends string> =
//try to see if prop is a number
Prop extends `${number}` ?
number extends keyof Value ? Value[number] :
unknown :
//check if prop is in value
Decode<Prop> extends keyof Value ? Value[Decode<Prop>] : unknown
export type PathExtract<Value, Path extends string> =
Extract<Value, SplitPath<Path>[0], SplitPath<Path>[1]>
export type Extract<Value, Prop, Rest extends string> =
Prop extends string ? PathExtract<ExtractProp<Value, Prop>, Rest> :
ExtractProp<Value, Rest>
amazing job! so we have template literals now in TS? been waiting for soo long!
wow this is cool!