When writing an interface to a dict like store, I'd like to distinguish data model and item in store, which is id & model. I want to add constrain that model itself does not used field id in their interface, but I don't know how to do that.
type Item<T> = T & {id: string}
// T is type of model
// Item<T> is type of objects in the store, use id as primary key
following function is simplified version, which is used to add new item in store
function add<T>(model: Item<T>): T {
const id = uuid();
const result = {...model, id};
store.push(result);
return result;
}
it's better to add some constrain on T that T does not have id property, otherwise T would be same as Item<T>, meanwhile the id in Item<T> is not the same one in T, which would lead to bugs.
As a summary, I need some type like this
type Item<T extends "The type which allow any type without property id: string"> = T & {id : string}
I've tried the following approaches:
type T0 = Exclude<any, {id: string}>; // any
type T1 = Exclude<{}, {id: string}>; // {}
type SafeModel<T extends Exclude<T, {id: string}>>; //circular constrain
None of them works.
I want some thing like
type Model // define like this
const v0: Model = {name: 'someName'} // ok
const v2: Model = {value: 123, id: 'someId'} //ok
const v1: Model = {name: 'someName', id: 'someId'} //error
or the way to bypass circular constrain to define type, which allows to define
type Model<T> = T extends { id: string } ? never: T;
type Item<T extends Model<T>> = T & { id: string }