#Using inferred type in template literal not working

1 messages · Page 1 of 1 (latest)

gleaming wren
#
interface User {
  id: number;
  username: string;
  password: string;
  email: string;
}

interface Order {
  id: number;
  user_id: number;
  quantity: number;
}


interface Tables {
  user: User;
  order: Order;
}


// Expected type of responseColumnMap = {name: 'user.username', qty: 'order.quantity'}
type responseColumnMap = {
  [key: string]: `${infer R}.${keyof Tables[R] & string}`
}

I am trying to create a map type using template literal syntax in which the values of the map will have type ${tableName}.${columnName}.
The columName should be constrained to allow only columns from their respective tables.
I tried inferring the table name and using that inferred type to create the type but I am getting infer' declarations are only permitted in the 'extends' clause of a conditional type. error.
How can I create the type that I am looking for?
I have attached the playground link with the minimal code.

Playground:
https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgKoGdrIN4ChnLAAmAXMiAK4C2ARtANz7IWZQhxURnphSgDmjAgAc46dAHcA9lFLIefEIKYQqcYABtuvAYwC+uXKEixEKAPKyseAsTKVaDJi2gB9O+Wp0oQ5AEcKOHBgMABPey8nA0NjaHgkZAAVOBoNCHQcZ1YyDCcCGSJoMktCn1xo3AB6SuQAUQAPYQgESCJkMKbkKRhkKHThKRBMAGEpDWoQAFk4YWQAXhwYYAgNIgBGMgByFygAOh32Tk2AGmQllaIAJi2C6F2AoLAQ0M3ojpQ+9AGhiFHxqimM3mmQIAG0ANYQcLyHRKAC6ZAABgASbCgGBYABKel2qMhoW6SRSaXQoMxcOQADIYYp+HpEeVcEA

carmine pastureBOT
#

@gleaming wren Here's a shortened URL of your playground link! You can remove the full link from your message.

AkhilKP#0327

Preview:```ts
interface User {
id: number;
username: string;
password: string;
email: string;
}

interface Order {
id: number;
user_id: number;
quantity: number;
}

interface Tables {
user: User;
order: Order;
}

// Expected type of responseColumnMap = {field1: 'user.username', field2: 'order.quantity'}
...```

median turtle
#

I don't quite understand why responseColumnMap should be the shape of { field1: 'user.username', field2: 'order.quantity' }.

#

Why couldn't it be { field1: 'user.email', field2: 'order.user_id' }? Which one should be field1 and which one should be field2?

hidden bronze
#

This seems to work

type AddPrefixToEachKey<Prefix extends string, Interface> =
  {[Field in string & keyof Interface as `${Prefix}${Field}`]: Interface[Field]}

type QualifiedKeys<Name extends string & keyof Tables> =
  Name extends Name // be distributive
    ? keyof AddPrefixToEachKey<`${Name}.`, Tables[Name]>
    : never;

type QualifiedField = QualifiedKeys<keyof Tables>;
// type QualifiedField = "user.id" | "user.username" | "user.password" | "user.email" | "order.id" | "order.user_id" | "order.quantity"```
median turtle
#

If they just want an union of paths to each fields, this works even recursively into nested types:

type QueryFieldOf<T, P extends string = ''> = {
  [K in keyof T]: T[K] extends number | string
    ? `${P}${K & string}`
    : QueryKeyOf<T[K], `${P}${K & string}.`>
}[keyof T]
#

But not sure what exactly is OP asking for.

gleaming wren
#

Hi @median turtle @hidden bronze , Sorry if my requirements were not clear in the original question.

Essentially what I want responseColumnMap to be a Map type with key as any string value and the value should of type '<table>.<column>'. So I want to type check that the column is actually present in the table.

For example:
'user.id', 'user.username', 'order.quantity' etc are all valid values.
But 'user.quantity' should not be allowed as value for responseColumnMap.

const responseColumnMap = {
  'randomString': 'user.email',
  'differentString': 'user.quantity' //Should throw type error as quantity is not part of User Interface
}

This is what I am trying to achieve.

median turtle
gleaming wren
#

@median turtle Thanks for the help. This is working for me. The only thing I don't understand from the solution is what is the purpose of [keyof T] at the very end of type QueryFieldOf

median turtle
#

Remove it and see 😉

gleaming wren
#

I tried removing it. Is it converting the map type to a union of allowed values by indexing with 'keyof T' ?

median turtle
#

Try walking through this step by step:

type Obj = {
    a: 'foo'
    b: 'bar'
}

type KeyOfObj = keyof Obj

type ValueOfObj = Obj[KeyOfObj]
gleaming wren
#

Okay. This made it clear for me. Thanks again for your help.