#What can prevent TS to properly narrow types on some cases ?

1 messages · Page 1 of 1 (latest)

trim violet
#

I'm not sure to properly understand to what extent is Typescript able to narrow types on some cases, and when it can't, how I can help it do so.

I have the following generic that resolves the type of property, matching its nested key.

type PickTypeFromPath<Type, Key> =
  Key extends `${infer Head extends keyof Type & string}.${infer Tail}` ? PickTypeFromPath<Type[Head], Tail>
  : Key extends keyof Type ? Type[Key]
  : never

It works as expected used in standalone, but when I add it as part of a more complex case, (being called from within an other generic), TS does not seem to be able to narrow down the key type, and properly infer the type associated to that property.

#

On the first example above, everything is working as expected. But on the 2nd, the inferred value is matching the possible type for any of the available keys at the root of the passed interfce, joined by a union.

fast anchorBOT
#
ChronicStone#1283

Preview:ts ... type PickTypeFromPath<Type, Key> = Key extends `${infer Head extends keyof Type & string}.${infer Tail}` ? PickTypeFromPath<Type[Head], Tail> : Key extends keyof Type ? Type[Key] : never ...

trim violet
#

I've made a complete playground of this with both cases setup. What's the principle behind TS's behaviour on this kind of case, and how can I get around the issue ?

#

What can prevent TS to properly narrow types on some cases ?

verbal wave
#

Hey, it cannot narrow because your Column<Entity>[] is unaware of which precise column you want the type of. Meaning, it will combine all columns in a single type.

I would either:

  • use a builder pattern ( for example buildSchemaFor<User>().withColumn('path', method) ) . The builder pattern will be able to get the precise type of the column
  • Not use a array for the columns, but a map
fast anchorBOT
#
Quadristan#2590

Preview:```ts
type Column<
Entity extends GenericObject,
Key = NestedPaths<DeepRequired<Entity>>

= {
// key: Key,
render: (
value: PickTypeFromPath<Entity, Key>
) => any
}

type PickTypeFromPath<Type, Key> =
Key extends ${infer Head extends keyof Type & string}.${infer Tail}
? PickTypeFromPath<Type[Head], Tail>
: Key
...```

inner tangle
#

well honestly

#

i don't know where Value is coming from

#

chances are you want to be using a cif:

#

!:cif

fast anchorBOT
#
Gerrit0#7591
`!gerrit0:cif`:

If you want to both validate that a variable matches a specific type and also let TypeScript narrow the variable if possible, a constrained identity function is usually the best way to do this:

interface Foo {
  foo: string | number;
}
function createFoo<T extends Foo>(x: T) { return x }

const foo = createFoo({ foo: 123 })
//    ^? const foo: { foo: number }
inner tangle
#

well, it can mostly be replaced by satisfies on TS4.9 and above

#

oh never mind

inner tangle
#

the second parameter will always be inferred which is why it's always a union

#

what you should use is this:

#

!hb disc