-
Notifications
You must be signed in to change notification settings - Fork 642
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
Typescript declaration size #1425
Comments
Make sure to use the recommended `export interface ITodo extends typeof Todo
.Type {}` for all of your model types, that might help.
…On Mon, Nov 18, 2019 at 7:58 PM Speros Kokenes ***@***.***> wrote:
*Question*
- I've checked documentation and searched for existing issues
- I tried the spectrum channel
<https://spectrum.chat/?t=dad48299-3dfc-4e10-b6da-9af1e39498a3>
I have a fairly complex set of models that form a big store. This store is
shared between a few services, so I have the store defined in its own
package that my other services import from.
I am trying to generate typescript declarations for the models in this
store so that my other services that use this store or sub models can get
the correct typings. I am not sure if I am doing something wrong but I am
getting a declaration file that is 7.4mb and about 109K LOC. This is
causing poor performance in my IDE as I make changes.
Looking at the declaration file, I can see that the resulting size is due
to reproducing the same type definitions over and over wherever they are
used in my model. For example, I have a model type called Expression that
is used by many other models in my store. This definition gets a unique
declaration in every model that is used, rather than reusing a single type
declaration for Expression.
My questions are:
1. Is this expected behavior?
2. Is there a way to avoid this so that I get slim, faster compiled
declarations?
3. Any other tips for how to architect or produce declarations for
this type of project?
Thanks
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#1425?email_source=notifications&email_token=AAN4NBBS7IQIGXAE3SQEQWDQULXU3A5CNFSM4JOZKWRKYY3PNVWWK3TUL52HS4DFUVEXG43VMWVGG33NNVSW45C7NFSM4H2ESFHQ>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAN4NBGXTXV4ZMTWYKCYUN3QULXU3ANCNFSM4JOZKWRA>
.
|
Make sure to specify concrete return types for views/actions which return other nodes, otherwise inferred return types are really huge. |
Those solutions don't seem to address my problem unfortunately. See the following example repo: In src, I create a store called
When I build the declarations file for these 3 models, Is there a reason that In a small model like this it doesn't really matter. But if you are building a complex store composed of many smaller stores, the declarations file quickly blows up as it reproduces store types over and over. For example, I am building a data analytics tool that uses MST. It has a complex store made up of smaller models, one of which is called |
See my PR, const ModelAInferred = t.model({
foo: t.string
});
type ModelAFactoryType = typeof ModelAInferred;
interface ModelAFactoryInterface extends ModelAFactoryType {}
export const ModelA: ModelAFactoryInterface = ModelAInferred;
export interface IModelAStore extends Instance<ModelAFactoryInterface> {}
const ModelBInferred = t.model({
a: ModelA
});
type ModelBFactoryType = typeof ModelBInferred;
interface ModelBFactoryInterface extends ModelBFactoryType {}
export const ModelB: ModelBFactoryInterface = ModelBInferred;
export interface IModelBStore extends Instance<ModelBFactoryInterface> {}
const ModelCInferred = t.model({
// using that will create another declaration!
// b: ModelBInferred
b: ModelB
});
type ModelCFactoryType = typeof ModelCInferred;
interface ModelCFactoryInterface extends ModelCFactoryType {}
export const ModelC: ModelCFactoryInterface = ModelCInferred;
export interface IModelCStore extends Instance<ModelCFactoryInterface> {} |
Output types, only one IModelType is emitted per created model factory import { IModelType, Instance, _NotCustomized, ISimpleType } from "mobx-state-tree";
declare const ModelAInferred: IModelType<{
foo: ISimpleType<string>;
}, {}, _NotCustomized, _NotCustomized>;
declare type ModelAFactoryType = typeof ModelAInferred;
interface ModelAFactoryInterface extends ModelAFactoryType {
}
export declare const ModelA: ModelAFactoryInterface;
export interface IModelAStore extends Instance<ModelAFactoryInterface> {
}
declare const ModelBInferred: IModelType<{
a: ModelAFactoryInterface;
}, {}, _NotCustomized, _NotCustomized>;
declare type ModelBFactoryType = typeof ModelBInferred;
interface ModelBFactoryInterface extends ModelBFactoryType {
}
export declare const ModelB: ModelBFactoryInterface;
export interface IModelBStore extends Instance<ModelBFactoryInterface> {
}
declare const ModelCInferred: IModelType<{
b: ModelBFactoryInterface;
}, {}, _NotCustomized, _NotCustomized>;
declare type ModelCFactoryType = typeof ModelCInferred;
interface ModelCFactoryInterface extends ModelCFactoryType {
}
export declare const ModelC: ModelCFactoryInterface;
export interface IModelCStore extends Instance<ModelCFactoryInterface> {
}
export {}; |
Awesome! Yes its a pain to have to write this out, but its way better than manually typing everything. Just curious, can you explain how it works? I don't totally follow why this behaves differently. |
I got to these kind of pattens a while ago when nested models killed my IDE. A related typescript issue is: |
I see, thanks. How are you handling union types? Those are still recreating some props for me, though not necessarily blowing things up for me completely since they at least are reusing nested complex types. Take the following input for example: type ForDirectExtend<T> = T;
const _ModelA = t.model({
foo: t.string
});
interface ModelAFactoryInterface extends ForDirectExtend<typeof _ModelA> {}
export const ModelA: ModelAFactoryInterface = _ModelA;
const _ModelB = t.model({
bar: t.number
});
interface ModelBFactoryInterface extends ForDirectExtend<typeof _ModelB> {}
export const ModelB: ModelBFactoryInterface = _ModelB;
export const ModelC = t.model({
a: ModelA
});
const _ModelD = t.union(ModelA, ModelB, ModelC);
interface ModelDFactoryInterface extends ForDirectExtend<typeof _ModelD> {}
export const ModelD: ModelDFactoryInterface = _ModelD; This produces the following declaration file: import { IModelType, _NotCustomized, ModelCreationType, ITypeUnion, ModelInstanceTypeProps, ModelSnapshotType, ISimpleType } from "mobx-state-tree";
declare type ForDirectExtend<T> = T;
declare const _ModelA: IModelType<{
foo: ISimpleType<string>;
}, {}, _NotCustomized, _NotCustomized>;
interface ModelAFactoryInterface extends ForDirectExtend<typeof _ModelA> {
}
export declare const ModelA: ModelAFactoryInterface;
declare const _ModelB: IModelType<{
bar: ISimpleType<number>;
}, {}, _NotCustomized, _NotCustomized>;
interface ModelBFactoryInterface extends ForDirectExtend<typeof _ModelB> {
}
export declare const ModelB: ModelBFactoryInterface;
export declare const ModelC: IModelType<{
a: ModelAFactoryInterface;
}, {}, _NotCustomized, _NotCustomized>;
declare const _ModelD: ITypeUnion<ModelCreationType<import("mobx-state-tree/dist/internal").ExtractCFromProps<{
foo: ISimpleType<string>;
}>> | ModelCreationType<import("mobx-state-tree/dist/internal").ExtractCFromProps<{
bar: ISimpleType<number>;
}>> | ModelCreationType<import("mobx-state-tree/dist/internal").ExtractCFromProps<{
a: ModelAFactoryInterface;
}>>, ModelSnapshotType<{
foo: ISimpleType<string>;
}> | ModelSnapshotType<{
bar: ISimpleType<number>;
}> | ModelSnapshotType<{
a: ModelAFactoryInterface;
}>, ModelInstanceTypeProps<{
foo: ISimpleType<string>;
}> | ModelInstanceTypeProps<{
bar: ISimpleType<number>;
}> | ModelInstanceTypeProps<{
a: ModelAFactoryInterface;
}>>;
interface ModelDFactoryInterface extends ForDirectExtend<typeof _ModelD> {
}
export declare const ModelD: ModelDFactoryInterface;
export {}; |
Yea I see what you mean. Another problem I am having is that I am exporting these models to be used by other libraries, but my models include flows which don't work for me when compiling to JS and importing into another library. I get a "Action requires parent context" error for any action called involving a flow. So, I have been exporting model factory functions instead that my other services can use to build the models. But I am not sure how to use this named type workaround with a factory, since I have to define the types inside of the factory function which makes it a private type. Any thoughts? |
I don't think it should happen. |
I'll look into that. I put together a sample repo showing this problem in a new issue if you're interested in taking a look and seeing if there is anything obviously wrong that I am doing with my setup #1427 |
I was having the same problem with union types, but I had some success by trying defining the output of the union using the other interfaces. ITypeUnion<
ModelCreationType2<PA, FCA> | ModelCreationType2<PB, FCB>,
ModelSnapshotType2<PA, FSA> | ModelSnapshotType2<PB, FSB>,
ModelInstanceType<PA, OA> | ModelInstanceType<PB, OB>
> So in order to leverage the predefined interfaces, I built upon your method above with the following: const ModelAInferred = t.model({/*...*/});
type ModelAFactoryType = typeof ModelAInferred;
interface ModelAFactoryInterface extends ModelAFactoryType {}
export const ModelA: ModelAFactoryInterface = ModelAInferred;
interface IModelACreationType extends SnapshotIn<ModelAFactoryInterface> {}
interface IModelASnapshot extends SnapshotOut<ModelAFactoryInterface> {}
interface IModelAStore extends Instance<ModelAFactoryInterface> {}
const ModelBInferred = t.model({/*...*/});
type ModelBFactoryType = typeof ModelBInferred;
interface ModelBFactoryInterface extends ModelBFactoryType {}
export const ModelB: ModelBFactoryInterface = ModelBInferred;
interface IModelBCreationType extends SnapshotIn<ModelBFactoryInterface> {}
interface IModelBSnapshot extends SnapshotOut<ModelBFactoryInterface> {}
interface IModelBStore extends Instance<ModelBFactoryInterface> {}
interface ModelABUnionFactoryInterface extends ITypeUnion<
IModelACreationType | IModelBCreationType,
IModelASnapshot | IModelBSnapshot,
IModelAStore | IModelBStore
> {}
export const ModelABUnion: ModelABUnionFactoryInterface = t.union(ModelA, ModelB);
type ModelABUnionStore = IModelAStore | IModelBStore; It worked pretty well; I have a union model that is part of another model, and with this change I significantly reduced the size of type declarations:
Probably this would also work just as well for >2 model unions (haven't tried), by just adding them to interface ModelABUnionFactoryInterface extends ITypeUnion<
ModelACType | ModelBCType | ModelCCType | /* ... */,
IModelASnapshot | IModelBSnapshot | ModelCSnapshot | /* ... */,
IModelAStore | IModelBStore | ModelCStore | /* ... */,
> {} Definitely pretty verbose, but, again, better than the alternative 😅 |
Interesting! |
I think i have a nice solution, but i need help with validation before we can make it into a PR function lazyInferenceTypeUnion<
M1 extends IAnyModelType,
M2 extends IAnyModelType,
M3 extends IAnyModelType,
>(m1: M1, m2: M2, m3: M3): LazyInferenceModelType<M1> | LazyInferenceModelType<M2> | LazyInferenceModelType<M3> {
return t.union(m1, m2, m3);
}
type LazyInferenceModelType<T extends IAnyModelType> = IType<ExtractProps<T>, ExtractOthers<T>, ExtractCSTWithoutSTN<T>>; See diff: which leads to: export declare const ModelD: LazyInferenceModelType<ModelAFactoryInterface> | LazyInferenceModelType<ModelBFactoryInterface> | LazyInferenceModelType<ModelCFactoryInterface>;
declare type LazyInferenceModelType<T extends IAnyModelType> = IType<ExtractProps<T>, ExtractOthers<T>, ExtractCSTWithoutSTN<T>>; |
I have a variadic variant of that function, but i'm not sure if its 100% correct types: function lazyInferenceTypeUnionVariadic<
ARGS extends Array<IAnyModelType>
>(...args: ARGS): LazyInferenceModelType<ARGS[number]> {
return t.union(...args);
} |
Hey @xaviergonz I would like your feedback for these union wrappers, import {
types as t,
Instance,
_NotCustomized,
IType
} from "mobx-state-tree";
import { ExtractProps, IAnyModelType, ExtractOthers, ExtractCSTWithoutSTN } from "mobx-state-tree/dist/internal";
type ForDirectExtend<T> = T;
const _ModelA = t.model({
foo: t.string
});
interface ModelAFactoryInterface extends ForDirectExtend<typeof _ModelA> {}
export const ModelA: ModelAFactoryInterface = _ModelA;
const _ModelB = t.model({
bar: t.number
});
interface ModelBFactoryInterface extends ForDirectExtend<typeof _ModelB> {}
export const ModelB: ModelBFactoryInterface = _ModelB;
export const _ModelC = t.model({
a: ModelA
});
interface ModelCFactoryInterface extends ForDirectExtend<typeof _ModelC> {}
export const ModelC: ModelCFactoryInterface = _ModelC;
const _ModelD = lazyInferenceTypeUnion(ModelA, ModelB, ModelC);
const variadicUnion = lazyInferenceTypeUnionVariadic(ModelA, ModelB, ModelC);
export const ModelD = _ModelD;
type IT = Instance<typeof variadicUnion>;
declare const bla: IT;
if ("foo" in bla) {
bla.foo.anchor
}
function lazyInferenceTypeUnion<
M1 extends IAnyModelType,
M2 extends IAnyModelType,
M3 extends IAnyModelType,
>(m1: M1, m2: M2, m3: M3): LazyInferenceModelType<M1> | LazyInferenceModelType<M2> | LazyInferenceModelType<M3> {
return t.union(m1, m2, m3);
}
function lazyInferenceTypeUnionVariadic<
ARGS extends Array<IAnyModelType>
>(...args: ARGS): LazyInferenceModelType<ARGS[number]> {
return t.union(...args);
}
type FilterOnly<T, N> = T extends N ? T : never;
function lazyInferenceTypeUnionVariadic3<
ARGS extends Array<IAnyModelType>
>(...args: ARGS): ({ [P in FilterOnly<keyof ARGS, number>]: LazyInferenceModelType<ARGS[P]> } )[number] {
return t.union(...args);
}
type LazyInferenceModelType<T extends IAnyModelType> = IType<ExtractProps<T>, ExtractOthers<T>, ExtractCSTWithoutSTN<T>>;
const result_lazyInferenceTypeUnionVariadic = lazyInferenceTypeUnionVariadic(ModelA, ModelB, ModelC);
const result_lazyInferenceTypeUnionVariadic3 = lazyInferenceTypeUnionVariadic3(ModelA, ModelB, ModelC); |
Following lessons learned in #1425 (comment)
Worth noting, |
Reduce size of composite mst types by exporting types interface See mobxjs/mobx-state-tree#1425 for details
Hey folks - it looks like we decided not to merge the proposed solution directly into MST, but it can be implemented by consuming apps, as per the discussion in #1562. Since it's been a while without activity here, I'm going to close out this issue. Thanks for all the good work here! |
Question
I have a fairly complex set of models that form a big store. This store is shared between a few services, so I have the store defined in its own package that my other services import from.
I am trying to generate typescript declarations for the models in this store so that my other services that use this store or sub models can get the correct typings. I am not sure if I am doing something wrong but I am getting a declaration file that is 7.4mb and about 109K LOC. This is causing poor performance in my IDE as I make changes.
Looking at the declaration file, I can see that the resulting size is due to reproducing the same type definitions over and over wherever they are used in my model. For example, I have a model type called
Expression
that is used by many other models in my store. This definition gets a unique declaration in every model that is used, rather than reusing a single type declaration forExpression
.My questions are:
Thanks
The text was updated successfully, but these errors were encountered: