Reputation: 1976
I've replaced many/most of my typescript entities with Immutable Records for some level of safety/security/warm and fuzzies but I just noticed that my constructor is now broken.
Originally, I was automatically creating new UUIDs in my constructor as a default value, but with the ImmutableJS Records - this behaviour is broken.
I do understand why, but I'm not entirely sure what the correct workaround is - but I feel like it's either really complicated, or stupidly simple.
import { Record } from "immutable";
import uuid from "uuid";
const tagDefaults: TagParams = {
depth: 0,
id: "tag::" + uuid.v4(),
name,
order: 0,
};
interface TagParams {
name: string;
order: number;
depth: number;
id: string;
}
export class Tag extends Record(tagDefaults) { }
Creating that initial tagDefaults
is what creates the first UUID - after which, all subsequent new Tag()
's use the same ID.
Is there a simple way to have a function called on each constructor? I've tried overriding in the constructor (this.id = uuid.v4())
but that actually causes Webpack to crap out on me.
UPDATE: June 8th, 2018
Using the answer @mpontus provided, here is an updated example showing that either option can work.
import { Record, Set } from "immutable";
import uuid from "uuid";
const tagDefaults: TagParams = {
depth: 0,
id: "",
name,
order: 0,
};
interface TagParams {
name: string;
order: number;
depth: number;
id: string;
}
export class Tag extends Record(tagDefaults) {
constructor(props: Partial<TagParams> = {}) {
// Option A - Works
if (!props.id) {
props.id = uuid.v4();
}
super(props);
// Option B - Works
// if (!this.id) {
// return this.set("id", uuid.v4());
// }
// return this;
}
}
describe("Given a Tag", () => {
describe("When constructed", () => {
test("It should contain a unique id", () => {
const tag1 = new Tag();
const tag2 = new Tag({});
const tag3 = new Tag({ depth: 1, name: "hello", order: 10 });
const tag4 = new Tag({ id: "tag4Id" });
const tags = Set([tag1, tag2, tag3, tag4].map((t) => t.id));
expect(tags.size).toBe(4);
console.log([tags]);
});
});
});
Upvotes: 1
Views: 342
Reputation: 2203
You can't accomplish what you want using default values.
Your attempt to override Tag constructor was good, but you have to override the values that go into the constructor:
const tagDefaults = {
depth: 0,
id: "",
name: "",
order: 0,
};
class Tag extends Record(tagDefaults) {
constructor(values) {
const finalValues = { ...values };
if (finalValues.id === undefined) {
finalValues.id = uuid.v4();
}
super(finalValues);
}
}
Alternatively, you can return a different record instance from the constructor, but I'm not sure if TypeScript will accept that.
const { Record } = require("immutable");
const uuid = require('uuid');
const tagDefaults = {
depth: 0,
id: undefined,
name: "",
order: 0,
};
class Tag extends Record(tagDefaults) {
constructor(values) {
super(values);
if (this.id === undefined) {
return this.set('id', uuid.v4());
}
return this;
}
}
Upvotes: 1