diff --git a/src/types/abstract_bone.d.ts b/src/types/abstract_bone.d.ts index 68dcca39..eec3c321 100644 --- a/src/types/abstract_bone.d.ts +++ b/src/types/abstract_bone.d.ts @@ -241,15 +241,15 @@ export class AbstractBone { * bone.attribute('foo'); // => 1 * bone.attribute('foo', 2); // => bone */ - attribute(this: T, name: Key, value: T[Key]): void; - attribute(this: T, name: Key): T[Key]; + attribute(this: T, name: Key, value: Literal): void; + attribute(this: T, name: Key): U extends Literal ? U : Literal; /** * Get the original attribute value. * @example * bone.attributeWas('foo') // => 1 */ - attributeWas>(this: T, key: Key): T[Key]; + attributeWas, U extends T[Key]>(this: T, key: Key): U extends Literal ? U : Literal; /** * See if attribute has been changed or not. diff --git a/test/types/basics.test.ts b/test/types/basics.test.ts index 861469b7..0ee9f9a9 100644 --- a/test/types/basics.test.ts +++ b/test/types/basics.test.ts @@ -48,7 +48,20 @@ describe('=> Basics (TypeScript)', function() { summary: string; @Column(TEXT) - settings: string; + get settings(): Record { + const text = this.attribute('settings') as string; + if (!text) return null; + try { + return JSON.parse(text); + } catch { + return null; + } + } + + set settings(value: string | Record) { + if (typeof value !== 'string') value = JSON.stringify(value); + this.attribute('settings', value); + } @Column() wordCount: number; @@ -81,12 +94,49 @@ describe('=> Basics (TypeScript)', function() { it('bone.attribute(name)', async function() { const post = await Post.create({ title: 'Cain' }); assert.equal(post.attribute('title'), 'Cain'); + assert.equal(post.attributeWas('title'), 'Cain'); + assert.equal(post.attribute('settings'), null); }); + it('bone.attribute(name) type casting', async function() { + class Article extends Bone { + @Column(DataTypes.TEXT) + get settings(): Record { + try { + // inferred type is Record, fallback to Literal + return JSON.parse(this.attribute('settings') as string); + } catch { + return null; + } + } + + @Column() + get isPrivate(): boolean { + return Boolean(this.attribute('isPrivate')); + } + + @Column(DataTypes.TEXT) + get callback(): () => void { + // inferred type is Function, fallback to Literal + return eval(this.attribute('callback') as string); + } + } + const article = new Article({}); + await Article.sync({}); + article.attribute('settings', '{"bar":2}'); + article.attribute('isPrivate', '1'); + article.attribute('callback', '() => 1'); + assert.deepEqual(article.settings, { bar: 2 }); + assert.equal(article.isPrivate, true); + assert.equal(article.callback(), 1); + }) + it('bone.attribute(name, value)', async function() { const post = new Post({}); post.attribute('title', 'Cain'); + post.attribute('settings', '{"foo":1}'); assert.equal(post.title, 'Cain'); + assert.deepEqual(post.settings, { foo: 1 }); }); it('bone.changed()', async function() {