Skip to content

Commit

Permalink
fix: this.attribute(name) should fallback to Literal (#353)
Browse files Browse the repository at this point in the history
```ts
class Foo {
  @column(TEXT)
  get settings(): Record<string, any> {
    // this.attribute('settings') should return the original union type here
    const text = this.attribute('settings') as string;
    if (!text) return null;
    try {
      return JSON.parse(text);
    } catch {
      return null;
    }
  }
}
```
  • Loading branch information
cyjake authored Sep 27, 2022
1 parent 6d82dc6 commit 8c1520a
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 4 deletions.
6 changes: 3 additions & 3 deletions src/types/abstract_bone.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,15 +241,15 @@ export class AbstractBone {
* bone.attribute('foo'); // => 1
* bone.attribute('foo', 2); // => bone
*/
attribute<T, Key extends keyof T>(this: T, name: Key, value: T[Key]): void;
attribute<T, Key extends keyof T>(this: T, name: Key): T[Key];
attribute<T, Key extends keyof T>(this: T, name: Key, value: Literal): void;
attribute<T, Key extends keyof T, U extends T[Key]>(this: T, name: Key): U extends Literal ? U : Literal;

/**
* Get the original attribute value.
* @example
* bone.attributeWas('foo') // => 1
*/
attributeWas<T, Key extends keyof Values<T>>(this: T, key: Key): T[Key];
attributeWas<T, Key extends keyof Values<T>, U extends T[Key]>(this: T, key: Key): U extends Literal ? U : Literal;

/**
* See if attribute has been changed or not.
Expand Down
52 changes: 51 additions & 1 deletion test/types/basics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,20 @@ describe('=> Basics (TypeScript)', function() {
summary: string;

@Column(TEXT)
settings: string;
get settings(): Record<string, any> {
const text = this.attribute('settings') as string;
if (!text) return null;
try {
return JSON.parse(text);
} catch {
return null;
}
}

set settings(value: string | Record<string, any>) {
if (typeof value !== 'string') value = JSON.stringify(value);
this.attribute('settings', value);
}

@Column()
wordCount: number;
Expand Down Expand Up @@ -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<string, any> {
try {
// inferred type is Record<string, any>, 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() {
Expand Down

0 comments on commit 8c1520a

Please sign in to comment.