Skip to content
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

refactor(data-types): convert data-types to TS #13799

Closed
wants to merge 3 commits into from
Closed

Conversation

wbourne0
Copy link
Member

@wbourne0 wbourne0 commented Dec 21, 2021

Pull Request Checklist

Please make sure to review and check all of these items:

  • Have you added new tests to prevent regressions?
  • Does npm run test or npm run test-DIALECT pass with this change (including linting)?
  • Is a documentation update included (if this change modifies existing APIs, or introduces new ones)?
  • Did you update the typescript typings accordingly (if applicable)?
  • Does the description below contain a link to an existing issue (Closes #[issue]) or a description of the issue you are solving?
  • Did you follow the commit message conventions explained in CONTRIBUTING.md?

Description Of Change

converts data-types to TS.

I've typed this in a way that the types of columns can be extracted from sequelize.define (in theroy - haven't gotten far enough to test that yet). I also added some generic types to assist in getting types of a DataType.

For any reviewers, I've labled 3 (ts) types which are relevant to each DataType:

  • "Acceptable" type (ie what it'll accept as user input, e.g. for dates this is numbers, Dates, or strings)
  • "Sane" type - What sequelize will normally return (e.g. for dates, Date)
  • "Raw" type - Whatever we need to deserealize for a type. (e.g. for a boolean this is a number, buffer, or string)

image

Child PRs

The approach of this PR

Due to the size of this change, I've opted to split up this PR into many to make it more reviewable.

Here's the steps I'll be taking:

  1. Create a "master" pr containing the changes which other data-types prs will be based on (this pr)
  2. For each dialect, create a PR based on this branch containing the needed changes.
  3. Once this PR is approved, merge in the child prs as they are approved.
  4. Once all of the dialect specific code is in (meaning: all lib/ code is approved and data-types should be working), start converting the tests to TS (in distinct prs, again for reviewability).
  5. Once all test prs are approved & merged, merge this pr (assuming tests are green)

@wbourne0 wbourne0 added type: typescript For issues and PRs. Things that involve typescript, such as typings and intellisense. status: wip For issues and PRs. Applied when the PR is not ready yet / when work to close the issue has started. labels Dec 21, 2021
@wbourne0 wbourne0 self-assigned this Dec 21, 2021
@WikiRik
Copy link
Member

WikiRik commented Dec 21, 2021

@allawesome497 checkout #12941 which was updated this week

@wbourne0
Copy link
Member Author

@allawesome497 checkout #12941 which was updated this week

Oh I completely missed that.

My change covers a few things that aren't handled in that pr such as named exports, inline static keys (e.g. key is then declared in the class itself rather than being exposed later), and more specific typing for types which vary from their raw values (in particular: "sane" vs "accepted" vs "raw" as I've called them).

@sdepold
Copy link
Member

sdepold commented Dec 21, 2021

@jesse23 @Keimeno Up for a review?

@sdepold
Copy link
Member

sdepold commented Dec 21, 2021

I think my TS skills don't really suffice to review this beast. I have an opinion the wording however:

"Acceptable" type (ie what it'll accept as user input, e.g. for dates this is numbers, Dates, or strings)
--> InputType

"Sane" type - What sequelize will normally return (e.g. for dates, Date)
--> SanitizedType

"Raw" type - Whatever we need to deserealize for a type. (e.g. for a boolean this is a number, buffer, or string)
--> OutputType

@sdepold
Copy link
Member

sdepold commented Dec 21, 2021

Adding @ephys as well :)

@jesse23
Copy link
Contributor

jesse23 commented Dec 21, 2021

@allawesome497 2 questions in general...

  • It is possible enable the test 1stly, then try to do pair converting with the test? For example in this case, no need to convert all impacted test but at least the major test/unit/sql/data-types.test.js.

  • Shall we merge with data-types.d.ts ( and eliminate data-types.d.ts eventually )?

Copy link
Member

@ephys ephys left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, this is my only note

return new Class(...args);
},
export function classToInvokable<
Class extends new (...args: Array<never>) => any
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it normal that args is Array<never>? Shouldn't it be Array<any> or Array<unknown>?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could also be Array<any> (and that's what I'd normally put). I just did this since eslint recommended it and I was unsure if it'd work. Will probably change it back to Array<any> since that's more intuitive IMO.

@wbourne0
Copy link
Member Author

I think my TS skills don't really suffice to review this beast. I have an opinion the wording however:

"Acceptable" type (ie what it'll accept as user input, e.g. for dates this is numbers, Dates, or strings) --> InputType

"Sane" type - What sequelize will normally return (e.g. for dates, Date) --> SanitizedType

"Raw" type - Whatever we need to deserealize for a type. (e.g. for a boolean this is a number, buffer, or string) --> OutputType

Sounds good, though I think that RawType is a bit clearer than OutputType since its more of an "internal" output type,

Boolean is one of the types where this is more relevant, since a "raw" boolean can be a buffer, string, or number imo.

@sdepold
Copy link
Member

sdepold commented Jan 9, 2022

How are we doing on this front? I just wanted to point out one more time, that we are now targeting v7 and could hence drop support for any weird hack that was introduced earlier.

@wbourne0
Copy link
Member Author

How are we doing on this front? I just wanted to point out one more time, that we are now targeting v7 and could hence drop support for any weird hack that was introduced earlier.

Have been pretty busy recently, back on it for now.

Looks like we'll need to re-model how DataTypes works but I don't think it'll be too bad (ie most standard usage should be fine iiuc).

@wbourne0 wbourne0 added type: refactor For issues and PRs. Things that improve the code readability, maintainability, testability, etc. hard For issues and PRs. labels Jan 19, 2022
@ephys
Copy link
Member

ephys commented Jan 19, 2022

I'm researching how we could support customising what type Sequelize must output for a given DataType. I'm waiting on this PR to be merged before properly experimenting but since you've knee-deep in DataTypes I'd like to know if my current draft would impact your decisions in this PR, like for SaneTypeOf https://gist.github.com/ephys/50a6a05be7322c64645be716fea6186a#support-for-alternative-js-types

Also I'd still like to bikeshed the names of the 3 types based on how I understood them:

  • "Acceptable" -> UserInputTypeOf
  • "Sane" SequelizeOutputTypeOf
  • "Raw" -> SqlInputTypeOf

@wbourne0
Copy link
Member Author

I'm researching how we could support customising what type Sequelize must output for a given DataType. I'm waiting on this PR to be merged before properly experimenting but since you've knee-deep in DataTypes I'd like to know if my current draft would impact your decisions in this PR, like for SaneTypeOf https://gist.github.com/ephys/50a6a05be7322c64645be716fea6186a#support-for-alternative-js-types

Also I'd still like to bikeshed the names of the 3 types based on how I understood them:

* "Acceptable" -> UserInputTypeOf

* "Sane" SequelizeOutputTypeOf

* "Raw" -> SqlInputTypeOf

Nope, I think your current draft is fine - though it will be a pain to migrate typing to this.

As I'm finding, there's a lot of undocumented dialect-specific logic around typing :/.

As for renaming: I will do this, I just haven't yet since there hasn't been a clear choice in terms of names yet (and I don't see this PR getting merged soon, given its scale so I figure its worth waiting).

@ephys
Copy link
Member

ephys commented Jan 21, 2022

though it will be a pain to migrate typing to this.

Yup! Not looking forward to it but I want the feature enough to be motivated at least

@wbourne0
Copy link
Member Author

wbourne0 commented Feb 10, 2022

@allawesome497 2 questions in general...

🤔 I thought I responded to this but I don't see any response.

It is possible enable the test 1stly, then try to do pair converting with the test? For example in this case, no need to convert all impacted test but at least the major test/unit/sql/data-types.test.js.

I've opted to convert everything to TS first and add tests later (however: this will be before this PR is merged). I added some more details to the PR description.

Shall we merge with data-types.d.ts ( and eliminate data-types.d.ts eventually )?

Yep! That should be done by the time I merge this if I haven't already removed the file.

@wbourne0
Copy link
Member Author

wbourne0 commented Mar 5, 2022

For renaming, thoughts on:

AcceptedType -> InputType
SaneType -> OutputType
RawType -> ParsedType

CC @sdepold @ephys

};
}

this.options = options;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be a good opportunity to normalize options. We should either have options, or _length & co. But not both, otherwise we need to keep both in sync and it's error prone.

constructor(precision: number, scale?: number);
constructor(...args: DecimalConstructorParams);
constructor(precision?: number | DecimalOptions, scale?: number) {
if (typeof precision === 'object') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use isObject from lodash instead

}

this._precision = this.options.precision;
this._scale = this.options.scale;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment about deduplicating options & instance fields

Comment on lines +666 to +667
public validate(value: AcceptedNumber): asserts value is AcceptedNumber;
public validate(value: AcceptedNumber): true {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't this be simplified to:

Suggested change
public validate(value: AcceptedNumber): asserts value is AcceptedNumber;
public validate(value: AcceptedNumber): true {
public validate(value: AcceptedNumber): asserts value is AcceptedNumber {

return 'DECIMAL';
}

public validate(value: AcceptedNumber): asserts value is AcceptedNumber;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AcceptedNumber doesn't include string. But DECIMAL should accept strings to avoid loss of precision

RawType = AcceptedType,
> {
public static readonly key: string = 'ABSTRACT';
// @internal
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't stripInternal only apply to jsdoc? (either way this should be jsdoc)

? Instance
: T;

export type AcceptableTypeOf<T extends DataType> =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the purpose of these different types should be documented


// If T is a constructor, returns the type of what `new T()` would return-
// otherwise, returns T
export type Constructed<T> = T extends abstract new () => infer Instance
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't this the same as the built-in type InstanceType?

import { logger } from '../../utils/logger';
import { validator as Validator } from '../../utils/validator-extras';

const warnings: Record<string, boolean> = {};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const warnings: Record<string, boolean> = {};
const warnings: Record<string, boolean> = Object.create(null);

@@ -0,0 +1,9 @@
export * from './dialects/abstract/data-types';
export * as db2 from './dialects/db2/data-types';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the reason for exposing these? Shouldn't users only use DataTypes.x and not DataTypes.postgres.x?

@ephys
Copy link
Member

ephys commented Mar 22, 2022

Some comments may be unrelated to this PR.

AcceptedType -> InputType
SaneType -> OutputType
RawType -> ParsedType

As long as they're well documented, I'm fine with it

};

Reflect.defineProperty(this, 'types', prop);
Reflect.defineProperty(this.prototype, 'types', prop);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to add it to the prototype too?

return null;
}

static readonly parse = this.prototype._sanitize;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to have the _sanitize instance method call the static parse method instead of getting something from the prototype


public toSql() {
if (this._precision || this._scale) {
return `DECIMAL(${[this._precision, this._scale]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if scale is specified but precision is not, we should throw with a clear error message

Comment on lines +408 to +415
case 'tiny':
return 'TINY_TEXT';
case 'medium':
return 'MEDIUM_TEXT';
case 'long':
return 'LONG_TEXT';
default:
return this.key;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why were these changed, they should be TINYTEXT & co

);
}

return true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should remove these return true from validate.

@WikiRik WikiRik mentioned this pull request Apr 21, 2022
6 tasks
@ephys ephys mentioned this pull request May 13, 2022
42 tasks
@wbourne0
Copy link
Member Author

Closing in favor of #14505.

@wbourne0 wbourne0 closed this Sep 16, 2022
@ephys ephys deleted the typeify-data-types branch October 3, 2022 16:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
hard For issues and PRs. status: wip For issues and PRs. Applied when the PR is not ready yet / when work to close the issue has started. type: refactor For issues and PRs. Things that improve the code readability, maintainability, testability, etc. type: typescript For issues and PRs. Things that involve typescript, such as typings and intellisense.
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

6 participants