Search code examples
typescripttypesconstantstypescript-typingstyping

How to write data with some type but export it as const


I would like to write some object that implement some type for now TestType but export it as read only const

type TestType = { someProperty: { childProperty: string } }

const obj1: TestType = { someProperty: { childProperty: 'my custom text' } } //  will works
const take1: typeof obj1['someProperty']['childProperty'] = '' // will except string

const obj2 = { someProperty: { childProperty: 'my custom text' } } as const
const take2: typeof obj2['someProperty']['childProperty'] = 'my custom text' // will except 'my custom text'

I want to get them both the object will implement the type but will be export as const

const obj3 = ({ someProperty: { childProperty: 'my custom text' } } as TestType) as const // will casue type error
const take3: typeof obj2['someProperty']['childProperty'] = 'my custom text' // will except 'my custom text'

Solution

  • You can do it using an identity function which is already constrained by another type.

    You'll still have to use an as const assertion on the input argument. This isn't very ergonomic, but it addresses your case:

    TS Playground

    function createConstrainedIdFn <Constraint extends Record<PropertyKey, unknown>>(): <T extends Constraint>(obj: T) => T {
      return obj => obj;
    }
    
    type TestType = { someProperty: { childProperty: string } };
    
    const createTestType = createConstrainedIdFn<TestType>();
    
    const obj1 = createTestType({ someProperty: { childProperty: 'my custom text' } } as const);
    
    const take1: typeof obj1['someProperty']['childProperty'] = 'another string' /*
          ^^^^^
    Type '"another string"' is not assignable to type '"my custom text"'.(2322) */
    
    

    You can make the syntax a bit more concise by using a shorter function name and immediately invoking the returned function:

    TS Playground

    function id <Constraint extends Record<PropertyKey, unknown>>(): <T extends Constraint>(obj: T) => T {
      return obj => obj;
    }
    
    type TestType = { someProperty: { childProperty: string } };
    
    const obj1 = id<TestType>()({ someProperty: { childProperty: 'my custom text' } } as const);