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'
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:
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:
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);