#Object with different types based on keys using string interpolation

1 messages · Page 1 of 1 (latest)

buoyant sleet
#

I want to be able to restrict the type of key values in an object, based in the type of the key. However, the type of the key is a string interpolation type.

What I want is any value in the object whose key includes an asterisk * should be (for sake of example) a number, and any other key should be a string.

const routes = {
 'some/route': 'a string',
 'with/a/*': 2
}

I have made a generic to check if a string has an asterisk which works well:

type IncludesAstrix<S> = S extends `${infer F}*${infer T}` ? true : false;

I can see it working:

type Truthy = IncludesAstrix<'with/a/*'> // true
type Falsey = IncludesAstrix<'no/asterisk'> // false

But it doesn't work when used with a key in an object:

type RoutesObject = {
  [K in string]: IncludesAstrix<K> extends true ? number : string;
};

With this, I'd expect this to error, but it doesn't:

type IncludesAstrix<S> = S extends `${infer F}*${infer T}` ? true : false;

type RoutesObject = {
  [K in string]: IncludesAstrix<K> extends true ? number : string;
};

const routes: RoutesObject = {
 'some/route': 'a string',
 'with/a/*': 'should not be string!' // this should error?
}

What am I doing wrong? Is this even possible?

steady mountainBOT
#
jamiecurnow#0

Preview:```ts
type IncludesAstrix<S> =
S extends ${infer F}*${infer T} ? true : false

type RoutesObject = {
[K in string]: IncludesAstrix<K> extends true
? number
: string
}

const routes: RoutesObject = {
"some/route": "a string",
"with/a/*": "should not be string!", // this should error?
...```

cerulean furnace
#

since your indexing operation is [K in string], K will always just be string, not something more specific, and since IncludesAstrix<string> is false it always goes down the else branch in that conditional type

#

if you hover over RoutesObject you can see what it reduces to

#

the "any other key should be a string" part of your requirements is going to be a fundamental issue. there's just no way to express the type "any string that doesn't contain a *" in typescript's type system

buoyant sleet
#

Ahh that toally makes sense. Thank you so much for the explination @cerulean furnace 🙌

cerulean furnace
#

if you have a set of statically-known keys though you could map over them. i guess i'm not exactly sure what your needs are

buoyant sleet
#

It's going to be a developer defined object in the end so the keys will not be known beforehand

cerulean furnace
#

would it be okay if the developer defined it by calling a function you provide?

#

if so, you could do something like this:

steady mountainBOT
#
mkantor#0

Preview:```ts
type IncludesAstrix<S> =
S extends ${string}*${string} ? true : false

type RoutesObject<K extends string> = {
[K1 in K]: IncludesAstrix<K1> extends true
? number
: string
}

const makeRoutes = <K extends string>(
routes: RoutesObject<K>
) => routes

makeRoutes({
...```