Giter Club home page Giter Club logo

Comments (18)

Qsppl avatar Qsppl commented on September 26, 2024 1

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.

from hybrids.

Qsppl avatar Qsppl commented on September 26, 2024 1

Problem solved.

I also sent to PR a version of the store() function, protected from passing arguments like store([SingletonStore]).

commit: 64777f5

from hybrids.

Qsppl avatar Qsppl commented on September 26, 2024

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/[email protected]"

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),
})

image

message:

Type 'Descriptor<IComponent, IModel>' is not assignable to type 'Property<IComponent, IModel | undefined>'.
  Type 'Descriptor<IComponent, IModel>' is not assignable to type 'Descriptor<IComponent, IModel | undefined>'.
    Types of property 'set' are incompatible.
      Type '((host: IComponent & HTMLElement, value: any, lastValue: IModel) => any) | undefined' is not assignable to type '((host: IComponent & HTMLElement, value: any, lastValue: IModel | undefined) => any) | undefined'.
        Type '(host: IComponent & HTMLElement, value: any, lastValue: IModel) => any' is not assignable to type '(host: IComponent & HTMLElement, value: any, lastValue: IModel | undefined) => any'.
          Types of parameters 'lastValue' and 'lastValue' are incompatible.
            Type 'IModel | undefined' is not assignable to type 'IModel'.
              Type 'undefined' is not assignable to type 'IModel'. ts(2322)

from hybrids.

smalluban avatar smalluban commented on September 26, 2024

Sadly, it's a breaking change in TypeScript v5.0.0

For now, please use the latest v4 version of TS. I suppose that changing the definition for the v5 will break it for earlier versions, so I am confused about what to do with that. Do you know if there is a way to specify the TS version, which is supported by the project?

from hybrids.

Qsppl avatar Qsppl commented on September 26, 2024

on typescript version: 4.9.5

  • first problem is solved
  • the second problem is not solved

from hybrids.

smalluban avatar smalluban commented on September 26, 2024

Add id: string to your IModel definition. Currently, the store() type is used for the singleton model definition.

from hybrids.

Qsppl avatar Qsppl commented on September 26, 2024

thanks, it works

from hybrids.

Qsppl avatar Qsppl commented on September 26, 2024

But if you use store(ModelStore, { id: 'modelId' }), then the problem appears again.

import { Model, define, store } from "https://esm.sh/[email protected]"

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' }),
})

from hybrids.

smalluban avatar smalluban commented on September 26, 2024

Ok, I added this case: c15da30

from hybrids.

Qsppl avatar Qsppl commented on September 26, 2024

thanks, it works

from hybrids.

Qsppl avatar Qsppl commented on September 26, 2024

This change causes an error when using store(ModelStore, { draft: true }).

import { Model, define, store } from "https://esm.sh/[email protected]"

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 }),
})

from hybrids.

Qsppl avatar Qsppl commented on September 26, 2024

Updated 20.03.2024 21:25

I seem to have solved this problem using overload:

// DELETED
// function store<E, M>(
//     Model: Model<M>,
// ): Descriptor<E, M extends { id: any } ? M | undefined : M>

// function store<E, M>(
//     Model: Model<M>,
//     options: {
//         id?: keyof E | ((host: E) => ModelIdentifier)
//         draft?: boolean
//     },
// ): Descriptor<E, M extends { id: any } ? M | undefined : M>

/** Enumerables and Listings */
export function store<E, M extends { id: any }>(
    Model: [Model<M>],
    options?: { id?: keyof E | ((host: E) => ModelIdentifier) },
): Descriptor<E, M[]>

/** Draft by Model */
export function store<E, M>(
    Model: Model<M>,
    options: { draft: true, id?: keyof E | ((host: E) => ModelIdentifier) },
): Descriptor<E, M>

/** Model */
export function store<E, M>(
    Model: Model<M>,
    options?: { draft?: false, id?: keyof E | ((host: E) => ModelIdentifier) },
): Descriptor<E, M extends { id: any } ? M | undefined : M>

Here is an example to run the test:

import { Model, define, store } from "https://esm.sh/[email protected]"

export interface ISingleton {
    prop1: number
}

export const SingletonStore: Model<ISingleton> = {
    prop1: 0
}

export interface IModel {
    id: number
    prop1: number
}

export const ModelStore: Model<IModel> = {
    id: true,
    prop1: 1
}

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
    singletonList: store([SingletonStore]),
    /// @ts-expect-error
    singletonDraftList: store([SingletonStore], { draft: true }),
    modelList: store([ModelStore]),
    modelDraftList: store([ModelStore], { draft: true }),

    /// @ts-expect-error
    singletonListById: store([SingletonStore], { id: 'modelId' }),
    /// @ts-expect-error
    singletonDraftListById: store([SingletonStore], { id: 'modelId', draft: true }),
    modelListById: store([ModelStore], { id: 'modelId' }),
    modelDraftListById: store([ModelStore], { id: 'modelId', draft: true }),
})

But I couldn't define the Model argument as an object, so the list of singletons goes into the corresponding overloads, but I expect that such an argument should return an error:
image

Please note that if you leave JsDoc comments, they will be displayed to users in the code.
image

from hybrids.

smalluban avatar smalluban commented on September 26, 2024

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

from hybrids.

Qsppl avatar Qsppl commented on September 26, 2024

I fixed the problem with the store([SingletonStore], ...) overload

/** 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/[email protected]"

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 }),
})

from hybrids.

Qsppl avatar Qsppl commented on September 26, 2024

Thank you, in the future I will send PRs for bugs that I can fix

from hybrids.

smalluban avatar smalluban commented on September 26, 2024

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 id: true for a listening case, but this is a value, not a type - It would require adding an additional type like EnumerableModel, which then forces to set id: true. I gave up .. :P

However, with this change and another fixed issue, it should be possible to use TS v5.0+ again. Let me know if I am wrong.

from hybrids.

Qsppl avatar Qsppl commented on September 26, 2024

I was trying to protect from passing a model definition without id: true for a listening case, but this is a value, not a type - It would require adding an additional type like EnumerableModel, which then forces to set id: true. I gave up .. :P

Here's proof that it works:

import { Model, ModelIdentifier } from "https://esm.sh/[email protected]"

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 })

image

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> = []

image

Here is proof that the problem is with the Model type:

import { Model, ModelIdentifier } from "https://esm.sh/[email protected]"

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 })

image

If in the same code you use any other type instead of Model, then everything works.

import { Model, ModelIdentifier } from "https://esm.sh/[email protected]"

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 })

image

from hybrids.

smalluban avatar smalluban commented on September 26, 2024

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 v8.2.14.

from hybrids.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.