Skip to content

Commit 9bcb375

Browse files
authored
feat: add casts (#1)
1 parent a2b9ccb commit 9bcb375

5 files changed

Lines changed: 62 additions & 24 deletions

File tree

.github/workflows/release.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ jobs:
88
runs-on: ubuntu-latest
99
steps:
1010
- name: Checkout
11-
uses: actions/checkout@v3
11+
uses: actions/checkout@v4
1212
- name: Update version
1313
run: echo "`jq '.version="${{github.ref_name}}"' package.json`" > package.json
1414
- name: Setup node
15-
uses: actions/setup-node@v3
15+
uses: actions/setup-node@v4
1616
with:
1717
node-version-file: ".nvmrc"
1818
- name: Install packages

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
"exports": {
2323
".": {
2424
"import": "./dist/json-model.js",
25-
"require": "./dist/json-model.umd.cjs"
25+
"require": "./dist/json-model.umd.cjs",
26+
"types": "./dist/index.d.ts"
2627
}
2728
},
2829
"scripts": {

src/index.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,2 @@
1-
import createModel from "@/model-factory";
2-
3-
export type * from "@/types";
1+
export * from "@/types";
42
export * from "@/model-factory";
5-
6-
export default createModel;

src/model-factory.ts

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,34 @@
11
import forgetKeys from "./lib/forgetKeys";
2-
import type { Config, ConfigDefinition, Model } from "./types";
2+
import type {
3+
CastFunction,
4+
Config,
5+
ConfigDefinition,
6+
DefaultCasts,
7+
Model,
8+
} from "./types";
9+
10+
const defaultCasts: Record<DefaultCasts, CastFunction> = {
11+
date: (date) =>
12+
typeof date === "string" || typeof date === "number"
13+
? new Date(date)
14+
: date,
15+
float: (float) =>
16+
typeof float === "number"
17+
? float
18+
: typeof float === "string"
19+
? parseFloat(float)
20+
: float,
21+
int: (int) =>
22+
typeof int === "number"
23+
? int
24+
: typeof int === "string"
25+
? parseInt(int)
26+
: int,
27+
string: (string) => (!string ? string : String(string)),
28+
};
329

430
let globalConfig: Config = {
31+
casts: {},
532
models: {},
633
ignoreAttributes: [],
734
resolveModelName: (model) => (model as any)?.type || "",
@@ -35,36 +62,49 @@ const resolveConfig = (baseConfig: Config, ...config: ConfigDefinition[]) =>
3562
);
3663

3764
export const createModel = <Data extends object = {}>(
38-
modelConfig: ConfigDefinition = {}
65+
modelConfig: ConfigDefinition<keyof Data> = {}
3966
) =>
4067
class BaseModel {
41-
constructor(data: Data, localConfig: ConfigDefinition = {}) {
68+
constructor(data: Data, localConfig: ConfigDefinition<keyof Data> = {}) {
4269
const {
4370
resolveModelName,
4471
resolveRelationshipNames,
4572
ignoreAttributes,
4673
models,
74+
casts,
4775
} = resolveConfig(globalConfig, modelConfig, localConfig);
4876
const relationships = resolveRelationshipNames(data);
4977
const attributes = forgetKeys(data, [
5078
...relationships,
5179
...ignoreAttributes,
5280
]);
5381

82+
for (const [key, cast] of Object.entries(casts)) {
83+
const resolvedCast =
84+
typeof cast === "string" ? defaultCasts[cast] : cast || (() => null);
85+
if ((attributes as any)[key]) {
86+
(attributes as any)[key] = resolvedCast((attributes as any)[key]);
87+
}
88+
}
89+
5490
Object.assign(this, attributes);
5591

5692
for (const relationship of relationships) {
5793
const relationData = data[relationship as keyof Data];
5894
const isMany = Array.isArray(relationData);
5995
const RelationshipModel =
60-
models[resolveModelName(isMany ? relationData?.[0] : relationData)];
96+
models[
97+
resolveModelName(
98+
isMany ? relationData?.[0] : relationData
99+
) as string
100+
];
61101

62102
if (isMany ? !relationData.length : !relationData) {
63103
return;
64104
}
65105

66106
if (!RelationshipModel) {
67-
console.error(`Model ${relationship} not registred.`);
107+
console.error(`Model ${relationship as string} not registred.`);
68108
return;
69109
}
70110

src/types.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
1-
export declare type ObjectKey = string | number;
1+
export declare type ObjectKey = string | number | symbol;
22

3-
export interface Config {
3+
export declare type CastFunction = (value: unknown) => unknown;
4+
export declare type DefaultCasts = "date" | "string" | "int" | "float";
5+
6+
export interface Config<CastKeys extends ObjectKey = ObjectKey> {
7+
casts: Partial<Record<CastKeys, DefaultCasts | CastFunction>>;
48
models: Record<string, Model<any>>;
59
ignoreAttributes: ObjectKey[];
610
resolveModelName: <M extends object>(model: M) => ObjectKey;
711
resolveRelationshipNames: <M extends object>(model: M) => ObjectKey[];
812
}
913

10-
export declare type ConfigDefinition =
11-
| ((config: Config) => Config)
12-
| Partial<Config>;
14+
export declare type ConfigDefinition<CastKeys extends ObjectKey = ObjectKey> =
15+
| ((config: Config<CastKeys>) => Config<CastKeys>)
16+
| Partial<Config<CastKeys>>;
1317

1418
export interface Model<Output extends object = {}> {
15-
new (data: Record<string, unknown>, localConfig?: ConfigDefinition): Output;
19+
new (data: object, localConfig?: ConfigDefinition<keyof Output>): Output;
1620

17-
create<
18-
T extends object,
19-
Data extends Record<string, unknown> | Record<string, unknown>[] = {}
20-
>(
21+
create<T extends object, Data extends object | object[] = {}>(
2122
this: new (...args: any[]) => T,
2223
data: Data,
23-
localConfig?: ConfigDefinition
24+
localConfig?: ConfigDefinition<keyof Output>
2425
): Data extends object[] ? (Output & T)[] : Output & T;
2526
}

0 commit comments

Comments
 (0)