#Infer constant type from interface

10 messages · Page 1 of 1 (latest)

desert vector
#

I am looking for a way to have an interface automatically get a constant type for some of the fields it has:

type Route = {
  path: string;
};

const FooRoute: Route = { path: 'foo' };

FooRoute.path // 'string', but want to have 'foo'

I've been reading about const Type Parameters (https://devblogs.microsoft.com/typescript/announcing-typescript-5-0/#const-type-parameters), but it looks like it's only possible for functions. I've also attempted to use readonly:

type Route = {
  readonly path: string;
};

const FooRoute: Route = { path: 'foo' };

FooRoute.path // still 'string'

I am aware that I could use satisfies and const:

type Route = {
  path: string;
};

const FooRoute = { path: 'foo' as const } satisfies Route;

FooRoute.path // 'foo', but not throught the interface

But this means I have to repeat this quite a lot, is it somehow solvable in the interface itself?

limpid cosmos
#

No way around it other than using a generic identity function:

type Route = {
    path: string
}

const makeRoute = <T extends string>(path: T) => ({ path } satisfies Route)

const fooRoute = makeRoute('foo')
#

!ts fooRoute

fallen craterBOT
#
const fooRoute: {
    path: "foo";
} /* 7:7 */```
limpid cosmos
#

Well technically generic identity function would be like this:

const makeRoute = <const T extends Route>(route: T) => route

But the other example also works and doesn't force readonly onto the return type.

desert vector
#

Hmmm, yeah hat seems to be the only solution then. Is there also a way to do this for other fields? For example, I also have an optional children property, and I would like it to have the references to the child routes as a constant as well.

#

For example:

type Route = {
  path: string;
  children?: Route[];
};

const makeRoute = <const T extends Route>(route: T) => route;

const FooRoute = makeRoute({ path: "foo" });
const BarRoute = makeRoute({ path: "bar" });
const RootRoute = makeRoute({ path: "", children: [FooRoute, BarRoute] });

RootRoute.children; // 'Route[]', excpected [FooRoute, BarRoute]
#

Ahhh, wait. I can just make the whole thing readonly

#
type Route = {
  path: string;
  children?: readonly Route[];
};

const makeRoute = <const T extends Route>(route: T) => route;

const FooRoute = makeRoute({ path: "foo" });
const BarRoute = makeRoute({ path: "bar" });
const RootRoute = makeRoute({ path: "", children: [FooRoute, BarRoute] });

RootRoute.children; //[FooRoute, BarRoute]
desert vector
#

Thanks @limpid cosmos, this solved my issue