-
Notifications
You must be signed in to change notification settings - Fork 86
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Using "store([Model])" causes a TypeScript error #236
Comments
In this case, an error also occurs. In reality, a handle can return undefined, but the types say that a handle always returns a model. import { Model, define, store } from "https://esm.sh/hybrids@8.2.11"
export interface IModel {
prop1: number
}
export const ModelStore: Model<IModel> = {
id: true,
prop1: 1
}
export interface IComponent extends HTMLElement {
model: undefined | IModel
}
export default define<IComponent>({
tag: "a-component",
model: store(ModelStore),
}) message:
|
Sadly, it's a breaking change in TypeScript For now, please use the latest |
I looked in other node modules: They all have the following content in package.json: {
...
"main": "",
"types": "index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/DefinitelyTyped/DefinitelyTyped.git",
"directory": "types/bootstrap"
},
"typesPublisherContentHash": "c92ed4379251cc173e28be43e85f18041aa3e6ec2cd0d7b66e800b0f8a0b36a8",
"typeScriptVersion": "4.5"
} I don’t know how it works, but most likely something similar is described in DefinitelyTyped documentation. |
on typescript version: 4.9.5
|
Add |
thanks, it works |
But if you use import { Model, define, store } from "https://esm.sh/hybrids@8.2.11"
export interface IModel {
id: number
prop1: number
}
export const ModelStore: Model<IModel> = {
id: true,
prop1: 1
}
export interface IComponent extends HTMLElement {
modelId: undefined | number
model: undefined | IModel
}
export default define<IComponent>({
tag: "a-component",
modelId: undefined,
model: store(ModelStore, { id: 'modelId' }),
}) |
Ok, I added this case: c15da30 |
thanks, it works |
This change causes an error when using import { Model, define, store } from "https://esm.sh/hybrids@8.2.11"
export interface IModel {
id: number
prop1: number
}
export const ModelStore: Model<IModel> = {
id: true,
prop1: 1
}
export interface IComponent extends HTMLElement {
modelId: undefined | number
model: undefined | IModel
draft: IModel
}
export default define<IComponent>({
tag: "a-component",
modelId: undefined,
model: store(ModelStore, { id: 'modelId' }),
draft: store(ModelStore, { draft: true }),
}) |
Good catch! I should add some tests for those types, it must be possible (I'll add this to my todo list). I updated the lastest commit with the overload version. You are always welcome to make a PR with a change, so you will have a chance to be a contributor ;) |
I fixed the problem with the /** Model */
function store<E, M extends { id: any } & object>(model: Model<M>, options?: { draft?: false, id?: keyof E | ((host: E) => number) }): Descriptor<E, M | undefined>
/** Model Draft */
function store<E, M extends { id: any } & object>(model: Model<M>, options: { draft: true, id?: keyof E | ((host: E) => number) }): Descriptor<E, M>
/** Model Listing */
function store<E, M extends { id: any } & object>(model: [Model<M>], options?: { draft?: false, id?: keyof E | ((host: E) => number) }): Descriptor<E, M[]>
/** Model Listing Draft */
function store<E, M extends { id: any } & object>(model: [Model<M>], options: { draft: true, id?: keyof E | ((host: E) => number) }): Descriptor<E, M[]>
/** Singleton */
function store<E, M extends { id?: never } & object>(model: Model<M> extends Array<any> ? never : Model<M>, options?: { draft?: false, id?: keyof E | ((host: E) => number) }): Descriptor<E, M>
/** Singleton Draft */
function store<E, M extends { id?: never } & object>(model: Model<M> extends Array<any> ? never : Model<M>, options: { draft: true, id?: keyof E | ((host: E) => number) }): Descriptor<E, M>
// Singleton Listing cannot exist! Here is a new typing test for all invariants of this function: import { Model, define, store } from "https://esm.sh/hybrids@8.2.11"
export interface ISingleton {
prop: number
length: number
}
export const SingletonStore: Model<ISingleton> = {
prop: 0,
length: 0,
}
export interface IModel {
id: number
prop: number
length: number
}
export const ModelStore: Model<IModel> = {
id: true,
prop: 0,
length: 0,
}
export interface IComponent extends HTMLElement {
modelId: undefined | number
singleton: ISingleton
singletonDraft: ISingleton
model: undefined | IModel
modelDraft: IModel
singletonById: ISingleton
singletonDraftById: ISingleton
modelById: undefined | IModel
modelDraftById: IModel
singletonList: ISingleton[]
singletonDraftList: ISingleton[]
modelList: IModel[]
modelDraftList: IModel[]
singletonListById: ISingleton[]
singletonDraftListById: ISingleton[]
modelListById: IModel[]
modelDraftListById: IModel[]
}
export default define<IComponent>({
tag: "a-component",
modelId: 0,
singleton: store(SingletonStore),
singletonDraft: store(SingletonStore, { draft: true }),
model: store(ModelStore),
modelDraft: store(ModelStore, { draft: true }),
singletonById: store(SingletonStore, { id: 'modelId' }),
singletonDraftById: store(SingletonStore, { id: 'modelId', draft: true }),
modelById: store(ModelStore, { id: 'modelId' }),
modelDraftById: store(ModelStore, { draft: true, id: 'modelId' }),
/// @ts-expect-error singleton cannot be list
singletonList: store([SingletonStore]),
/// @ts-expect-error singleton cannot be list
singletonDraftList: store([SingletonStore], { draft: true }),
modelList: store([ModelStore]),
modelDraftList: store([ModelStore], { draft: true }),
/// @ts-expect-error singleton cannot be list
singletonListById: store([SingletonStore], { id: 'modelId' }),
/// @ts-expect-error singleton cannot be list
singletonDraftListById: store([SingletonStore], { id: 'modelId', draft: true }),
modelListById: store([ModelStore], { id: 'modelId' }),
modelDraftListById: store([ModelStore], { id: 'modelId', draft: true }),
}) |
Thank you, in the future I will send PRs for bugs that I can fix |
Your solution is almost perfect - listing does not work with draft, so there are 5 cases, not 6: // Enumerable
function store<E, M extends { id: string } & object>(
model: Model<M>,
options?: { draft?: false; id?: keyof E | ((host: E) => number) },
): Descriptor<E, M | undefined>;
// Enumerable Draft
function store<E, M extends { id: string } & object>(
model: Model<M>,
options: { draft: true; id?: keyof E | ((host: E) => number) },
): Descriptor<E, M>;
// Enumerable Listing
function store<E, M extends { id: string } & object>(
model: [Model<M>],
options?: { draft?: false; id?: keyof E | ((host: E) => number) },
): Descriptor<E, M[]>;
// Singleton
function store<E, M extends { id?: never } & object>(
model: Model<M> extends Array<any> ? never : Model<M>,
options?: { draft?: false; id?: keyof E | ((host: E) => number) },
): Descriptor<E, M>;
// Singleton Draft
function store<E, M extends { id?: never } & object>(
model: Model<M> extends Array<any> ? never : Model<M>,
options: { draft: true; id?: keyof E | ((host: E) => number) },
): Descriptor<E, M>; I was trying to protect from passing a model definition without However, with this change and another fixed issue, it should be possible to use TS |
Here's proof that it works: import { Model, ModelIdentifier } from "https://esm.sh/hybrids@8.2.11"
interface IEnumerableModel { id: any, prop: number }
interface ISingletonModel { prop: number }
// ################################### STORE BY `{ ... }` ###################################
const EnumerableInstance: IEnumerableModel = { id: true, prop: 0 }
const SingletonInstance: ISingletonModel = { prop: 0 }
/** Model */
function store<E, M extends { id: any } & object>(model: M, options?: { draft?: false, id?: keyof E | ((host: E) => ModelIdentifier) }): M | undefined
/** Model Draft */
function store<E, M extends { id: any } & object>(model: M, options: { draft: true, id?: keyof E | ((host: E) => ModelIdentifier) }): M
/** Model Listing */
function store<E, M extends { id: any } & object>(model: [M], options?: { id?: keyof E | ((host: E) => ModelIdentifier), loose?: boolean }): [M]
// Model Listing Draft cannot exist!
/** Singleton */
function store<E, M extends { id?: never } & object>(model: M extends Array<any> ? never : M, options?: { draft?: false, id?: keyof E | ((host: E) => ModelIdentifier) }): M
/** Singleton Draft */
function store<E, M extends { id?: never } & object>(model: M extends Array<any> ? never : M, options: { draft: true, id?: keyof E | ((host: E) => ModelIdentifier) }): M
// Singleton listing cannot exist!
function store(model: any, options?: any): any { }
const modelId: undefined | number = 0
const singleton: ISingletonModel = store(SingletonInstance)
const singletonDraft: ISingletonModel = store(SingletonInstance, { draft: true })
const model: undefined | IEnumerableModel = store(EnumerableInstance)
const modelDraft: IEnumerableModel = store(EnumerableInstance, { draft: true })
const singletonById: ISingletonModel = store(SingletonInstance, { id: 'modelId' })
const singletonDraftById: ISingletonModel = store(SingletonInstance, { id: 'modelId', draft: true })
const modelById: undefined | IEnumerableModel = store(EnumerableInstance, { id: 'modelId' })
const modelDraftById: IEnumerableModel = store(EnumerableInstance, { draft: true, id: 'modelId' })
const singletonList: ISingletonModel[] = store([SingletonInstance])
const singletonDraftList: ISingletonModel[] = store([SingletonInstance], { draft: true })
const modelList: IEnumerableModel[] = store([EnumerableInstance])
const modelDraftList: IEnumerableModel[] = store([EnumerableInstance], { draft: true })
const singletonListById: ISingletonModel[] = store([SingletonInstance], { id: 'modelId' })
const singletonDraftListById: ISingletonModel[] = store([SingletonInstance], { id: 'modelId', draft: true })
const modelListById: IEnumerableModel[] = store([EnumerableInstance], { id: 'modelId' })
const modelDraftListById: IEnumerableModel[] = store([EnumerableInstance], { id: 'modelId', draft: true }) You can't do this because of another bug: // This does not cause an error!
// This code should return the error "Model cannot be an array"
// Due to the fact that Model can be an array, typing breaks down in other places!
const Model: Model<any> = [] Here is proof that the problem is with the Model type: import { Model, ModelIdentifier } from "https://esm.sh/hybrids@8.2.11"
interface IEnumerableModel { id: any, prop: number }
interface ISingletonModel { prop: number }
// ################################### STORE BY `Model<{ ... }>` ###################################
const EnumerableStore: Model<IEnumerableModel> = { id: true, prop: 0 }
const SingletonStore: Model<ISingletonModel> = { prop: 0 }
// This does not cause an error!
// This code should return the error "Model cannot be an array"
// Due to the fact that Model can be an array, typing breaks down in other places!
const Model: Model<any> = []
/** Model */
function store<E, M extends { id: any } & object = never>(model: Model<M>, options?: { draft?: false, id?: keyof E | ((host: E) => ModelIdentifier) }): M | undefined
/** Model Draft */
function store<E, M extends { id: any } & object = never>(model: Model<M>, options: { draft: true, id?: keyof E | ((host: E) => ModelIdentifier) }): M
/** Model Listing */
function store<E, M extends { id: any } & object>(model: [Model<M>], options?: { draft?: false, id?: keyof E | ((host: E) => ModelIdentifier), loose?: boolean }): [M]
// Model Listing Draft cannot exist!
/** Singleton */
function store<E, M extends { id?: never } & object>(model: M extends Array<any> ? never : Model<M>, options?: { draft?: false, id?: keyof E | ((host: E) => ModelIdentifier) }): M
/** Singleton Draft */
function store<E, M extends { id?: never } & object>(model: M extends Array<any> ? never : Model<M>, options: { draft: true, id?: keyof E | ((host: E) => ModelIdentifier) }): M
// Singleton listing cannot exist!
function store(model: any, options?: any): any { }
const modelId: undefined | number = 0
const singleton: ISingletonModel = store(SingletonStore)
const singletonDraft: ISingletonModel = store(SingletonStore, { draft: true })
const model: undefined | IEnumerableModel = store(EnumerableStore)
const modelDraft: IEnumerableModel = store(EnumerableStore, { draft: true })
const singletonById: ISingletonModel = store(SingletonStore, { id: 'modelId' })
const singletonDraftById: ISingletonModel = store(SingletonStore, { id: 'modelId', draft: true })
const modelById: undefined | IEnumerableModel = store(EnumerableStore, { id: 'modelId' })
const modelDraftById: IEnumerableModel = store(EnumerableStore, { draft: true, id: 'modelId' })
const singletonList: ISingletonModel[] = store([SingletonStore])
const singletonDraftList: ISingletonModel[] = store([SingletonStore], { draft: true })
const modelList: IEnumerableModel[] = store([EnumerableStore])
const modelDraftList: IEnumerableModel[] = store([EnumerableStore], { draft: true })
const singletonListById: ISingletonModel[] = store([SingletonStore], { id: 'modelId' })
const singletonDraftListById: ISingletonModel[] = store([SingletonStore], { id: 'modelId', draft: true })
const modelListById: IEnumerableModel[] = store([EnumerableStore], { id: 'modelId' })
const modelDraftListById: IEnumerableModel[] = store([EnumerableStore], { id: 'modelId', draft: true }) If in the same code you use any other type instead of Model, then everything works. import { Model, ModelIdentifier } from "https://esm.sh/hybrids@8.2.11"
interface IEnumerableModel { id: any, prop: number }
interface ISingletonModel { prop: number }
// ################################### STORE BY `Type<{ ... }>` ###################################
type Type<M> = { a: M }
const EnumerableInstance: Type<IEnumerableModel> = { a: { id: true, prop: 0 } }
const SingletonInstance: Type<ISingletonModel> = { a: { prop: 0 } }
/** Model */
function store<E, M extends { id: any } & object>(model: Type<M>, options?: { draft?: false, id?: keyof E | ((host: E) => ModelIdentifier) }): M | undefined
/** Model Draft */
function store<E, M extends { id: any } & object>(model: Type<M>, options: { draft: true, id?: keyof E | ((host: E) => ModelIdentifier) }): M
/** Model Listing */
function store<E, M extends { id: any } & object>(model: [Type<M>], options?: { draft?: false, id?: keyof E | ((host: E) => ModelIdentifier), loose?: boolean }): [M]
// Model Listing Draft cannot exist!
/** Singleton */
function store<E, M extends { id?: never } & object>(model: M extends Array<any> ? never : Type<M>, options?: { draft?: false, id?: keyof E | ((host: E) => ModelIdentifier) }): M
/** Singleton Draft */
function store<E, M extends { id?: never } & object>(model: M extends Array<any> ? never : Type<M>, options: { draft: true, id?: keyof E | ((host: E) => ModelIdentifier) }): M
// Singleton listing cannot exist!
function store(model: any, options?: any): any { }
const modelId: undefined | number = 0
const singleton: ISingletonModel = store(SingletonInstance)
const singletonDraft: ISingletonModel = store(SingletonInstance, { draft: true })
const model: undefined | IEnumerableModel = store(EnumerableInstance)
const modelDraft: IEnumerableModel = store(EnumerableInstance, { draft: true })
const singletonById: ISingletonModel = store(SingletonInstance, { id: 'modelId' })
const singletonDraftById: ISingletonModel = store(SingletonInstance, { id: 'modelId', draft: true })
const modelById: undefined | IEnumerableModel = store(EnumerableInstance, { id: 'modelId' })
const modelDraftById: IEnumerableModel = store(EnumerableInstance, { draft: true, id: 'modelId' })
const singletonList: ISingletonModel[] = store([SingletonInstance])
const singletonDraftList: ISingletonModel[] = store([SingletonInstance], { draft: true })
const modelList: IEnumerableModel[] = store([EnumerableInstance])
const modelDraftList: IEnumerableModel[] = store([EnumerableInstance], { draft: true })
const singletonListById: ISingletonModel[] = store([SingletonInstance], { id: 'modelId' })
const singletonDraftListById: ISingletonModel[] = store([SingletonInstance], { id: 'modelId', draft: true })
const modelListById: IEnumerableModel[] = store([EnumerableInstance], { id: 'modelId' })
const modelDraftListById: IEnumerableModel[] = store([EnumerableInstance], { id: 'modelId', draft: true }) |
In short, for me, the most important is to ensure that the correct case works in TypeScript - this is essentially required to make it work with TS. Preventing from passing wrong arguments is great, but I would say "nice to have". The library protects from it and throws errors in runtime. BTW, Your examples are so detailed, that I see them hard to reason about and focus on a problem that you want to address. There were some bugs in the latest changes, so other stuff missing, please try out |
Problem solved. I also sent to PR a version of the commit: 64777f5 |
message:
tsconfig.json
typescript version: "5.1.6"
The text was updated successfully, but these errors were encountered: