'as const' combined with type?

Viewed 8886

I want to combine both having a type for a constant, and using it "as const" to get the literal types as the type:

type MyType = {name: string};

const x:MyType = {
    name: 'test' // Autocompleted, typesafe. But Type is {name: string}, not 
                 // what I want, {name: 'test'}
}

const x = { name: 'test' } as const; // Gives correct type, but no type check...

How to do this?

3 Answers

Here is a way to achieve what you want:

type MyType = { name: string }

// This function does nothing. It just helps with typing
const makeMyType = <T extends MyType>(o: T) => o

const x = makeMyType({
  name: 'test', // Autocompleted, typesafe
} as const)

x.name // type is 'test' (not string)

There is no need to type-check {name: "test"} as const because Typescript uses structural equality meaning aslong as {name: "test"} as const is the same structure of MyType they will be equal and therefore the same.

This behaviour can be observed using these helper types.

interface MyType {
     name: string;
}
const test = {name: "test"} as const;
type IsEqual<T, U> = [T] extends [U] ? true : false;
type AreTheyEqual = IsEqual<typeof test, MyType> // true they are the same.

Anything that takes a MyType can take a typeof Test.

EDIT: If you want to force test to be of type MyType to type-check test there you cannot do this by keeping the string literal because anything asserted to be MyType will lose the literal type and fall back to string this behaviour can be observed here.

type MyType = {name: string};
const x:MyType = {
    name: 'test' as const
}
type Test = typeof x["name"] // string;

Meaning if you want to have both Literal and string types on MyType you will need to instead do something like this (changing MyType). Note using this seems verbose but is arguably less boilerplate than "as const"

interface MyType<NAME extends string = string> {
    name: NAME;
}
const x: MyType = {
    name: 'test' // string;
}
const y: MyType<"test"> = {
    name: "test" // "test"
}

type Test1 = typeof x["name"]// string;
type Test = typeof y["name"] // "test";

^ Both string and literal types allowed.

I don’t think there is any primitive support. But you can do typechecking by defining another const with type assertion.

type MyType = {name: string};

const x = {
    name: 'test' 
} as const

const xTypeCheck = typeof (x as MyType) // Check the type of x as MyType 

Note:I use 'typeof' for avoiding reference of x's value which ensures that x's value can be deleted if there is not any other reference.

Related