Skip to content
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

fix: revert 2218 to fix ts excessively deep types #2234

Merged
merged 2 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion __tests__/core/2230.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//github.com/mobxjs/mobx-state-tree/issues/2230

import { describe, test } from "bun:test"
import { types, Instance } from "../../src/index"

Expand Down Expand Up @@ -150,6 +149,31 @@ describe("2230 - type instantiation is excessively deep and possibly infinite",
return self.prop41
}
}))
.actions((self: IAction4) => ({
getProp51(): string {
return self.prop51
}
}))
.actions((self: IAction4) => ({
getProp61(): string {
return self.prop61
}
}))
.actions((self: IAction4) => ({
getProp71(): string {
return self.prop71
}
}))
.actions((self: IAction4) => ({
getProp81(): string {
return self.prop81
}
}))
.actions((self: IAction4) => ({
getProp91(): string {
return self.prop91
}
}))
interface IAction5 extends Instance<typeof Action5> {}
})
})
81 changes: 0 additions & 81 deletions __tests__/core/type-system.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1417,84 +1417,3 @@ test("#1627 - union dispatch function is typed", () => {
types.null
)
})

test("#2216 - should respect optionality when extending another type", () => {
const Base = types.model("ErrorStore", { value: types.string }).extend(self => ({
actions: {
setValue(value?: string): boolean {
self.value = value || "test"
return true
},

setAnotherValue(value?: string): boolean {
self.value = value || "test"
return true
}
},
views: {
get spam(): string {
return self.value
},

get eggs(): string {
return self.value
}
},
state: {
anotherValue: "test" as string,
soManyValues: "test" as string
}
}))

const Extended = Base.named("Extended")
.props({
value: "test"
})
.extend(self => ({
actions: {
setValue(value: string): number {
self.value = value
return value.length
}
},
views: {
get spam(): boolean {
return !!self.value
}
},
state: {
anotherValue: "test" as string | undefined
}
}))
.actions(self => ({
setAnotherValue(value: string): number {
self.value = value
return value.length
}
}))
.views(self => ({
get eggs(): boolean {
return !!self.value
}
}))
.volatile(self => ({
soManyValues: "test" as string | undefined
}))

type InputSnapshot = SnapshotIn<typeof Extended>
type InstanceType = Instance<typeof Extended>

assertTypesEqual(_ as InputSnapshot, _ as { value?: string })
assertTypesEqual(
_ as InstanceType,
_ as {
value: string
setValue(value: string): number
setAnotherValue(value: string): number
spam: boolean
eggs: boolean
anotherValue: string | undefined
soManyValues: string | undefined
}
)
})
36 changes: 13 additions & 23 deletions src/types/complex-types/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,6 @@ export interface ModelPropertiesDeclaration {
[key: string]: ModelPrimitive | IAnyType
}

/** intersect two object types, but omit keys of B from A before doing so */
type OmitMerge<A, B> = {
[K in keyof A as K extends keyof B ? never : K]: A[K]
} & B

/**
* Unmaps syntax property declarations to a map of { propName: IType }
*
Expand Down Expand Up @@ -205,28 +200,23 @@ export interface IModelType<
// so it is recommended to use pre/post process snapshot after all props have been defined
props<PROPS2 extends ModelPropertiesDeclaration>(
props: PROPS2
): IModelType<
OmitMerge<PROPS, ModelPropertiesDeclarationToProperties<PROPS2>>,
OTHERS,
CustomC,
CustomS
>
): IModelType<PROPS & ModelPropertiesDeclarationToProperties<PROPS2>, OTHERS, CustomC, CustomS>

views<V extends AnyObject>(
views<V extends Object>(
fn: (self: Instance<this>) => V
): IModelType<PROPS, OmitMerge<OTHERS, V>, CustomC, CustomS>
): IModelType<PROPS, OTHERS & V, CustomC, CustomS>

actions<A extends ModelActions>(
fn: (self: Instance<this>) => A
): IModelType<PROPS, OmitMerge<OTHERS, A>, CustomC, CustomS>
): IModelType<PROPS, OTHERS & A, CustomC, CustomS>

volatile<VS extends AnyObject>(
fn: (self: Instance<this>) => VS
): IModelType<PROPS, OmitMerge<OTHERS, VS>, CustomC, CustomS>
volatile<TP extends object>(
fn: (self: Instance<this>) => TP
): IModelType<PROPS, OTHERS & TP, CustomC, CustomS>

extend<A extends ModelActions = {}, V extends AnyObject = {}, VS extends AnyObject = {}>(
extend<A extends ModelActions = {}, V extends Object = {}, VS extends Object = {}>(
fn: (self: Instance<this>) => { actions?: A; views?: V; state?: VS }
): IModelType<PROPS, OmitMerge<OTHERS, A & V & VS>, CustomC, CustomS>
): IModelType<PROPS, OTHERS & A & V & VS, CustomC, CustomS>

preProcessSnapshot<NewC = ModelCreationType2<PROPS, CustomC>>(
fn: (snapshot: NewC) => WithAdditionalProperties<ModelCreationType2<PROPS, CustomC>>
Expand Down Expand Up @@ -477,7 +467,7 @@ export class ModelType<
return this.cloneAndEnhance({ properties })
}

volatile<TP extends AnyObject>(fn: (self: Instance<this>) => TP) {
volatile<TP extends object>(fn: (self: Instance<this>) => TP) {
if (typeof fn !== "function") {
throw new MstError(
`You passed an ${typeof fn} to volatile state as an argument, when function is expected`
Expand Down Expand Up @@ -514,7 +504,7 @@ export class ModelType<
})
}

extend<A extends ModelActions = {}, V extends AnyObject = {}, VS extends AnyObject = {}>(
extend<A extends ModelActions = {}, V extends Object = {}, VS extends Object = {}>(
fn: (self: Instance<this>) => { actions?: A; views?: V; state?: VS }
) {
const initializer = (self: Instance<this>) => {
Expand All @@ -531,15 +521,15 @@ export class ModelType<
return this.cloneAndEnhance({ initializers: [initializer] })
}

views<V extends AnyObject>(fn: (self: Instance<this>) => V) {
views<V extends Object>(fn: (self: Instance<this>) => V) {
const viewInitializer = (self: Instance<this>) => {
this.instantiateViews(self, fn(self))
return self
}
return this.cloneAndEnhance({ initializers: [viewInitializer] })
}

private instantiateViews(self: this["T"], views: AnyObject): void {
private instantiateViews(self: this["T"], views: Object): void {
// check views return
if (!isPlainObject(views)) {
throw new MstError(`views initializer should return a plain object containing views`)
Expand Down