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

typescriptの型に関して: any型定義を減らしてほしい #16

Open
rktyt opened this issue Mar 5, 2021 · 1 comment
Open
Labels
pkg: dts-gen @kintone/dts-gen

Comments

@rktyt
Copy link

rktyt commented Mar 5, 2021

Package(対象パッケージ)

  • @kintone/dts-gen

Why(なぜ)

eslintで@typescript-eslint/no-unsafe-***のルールを有効化しにくいため

What(何を)

型定義をより厳密にしてもらいたい。
最悪 any を unknown に変えるでも。。

pulgin開発時

tsconfig.jsonに

"files": ["node_modules/@kintone/dts-gen/kintone.d.ts"],

指定で利用すると結構lintエラーになってしまう

利用している.eslintrc.ymlの一部

extends:
  #- '@cybozu/eslint-config'
  - eslint:recommended
  - plugin:import/errors
  - plugin:import/warnings
  - plugin:import/typescript
  - plugin:@typescript-eslint/recommended
  - plugin:@typescript-eslint/recommended-requiring-type-checking
  - prettier
rules:
  import/first: error
  import/newline-after-import: error
  'no-unused-vars': 'off'
  '@typescript-eslint/no-unused-vars': 'off'
  'require-await': 'off'
  '@typescript-eslint/require-await': 'off'

関係するnode modulesの利用バージョン

    "@typescript-eslint/eslint-plugin": "^4.16.1",
    "@typescript-eslint/parser": "^4.16.1",
    "eslint": "^7.21.0",
    "eslint-config-prettier": "^8.1.0",
    "eslint-plugin-import": "^2.22.1",
    "prettier": "^2.2.1",
    "typescript": "^4.2.3",

一応手元で@kintone/dts-gen/kintone.d.tsを調整したもの
(使う部分しか定義してません)
※もっと良い定義の仕方はあると思われます。

declare namespace kintone {
  export const $PLUGIN_ID: string

  namespace plugin {
    namespace app {
      function getConfig(pluginId: string): Record<string, string>
      function setConfig(
        config: Record<string, string>,
        callback?: () => void,
      ): void

      namespace proxyConfig {
        type Headers = Record<string, string>
        type Data = Record<string, unknown>
        type Response = {
          headers: Headers
          data: Data
        }
      }

      function getProxyConfig(url: string, method: string): proxyConfig.Response

      function setProxyConfig(
        url: string,
        method: string,
        headers: proxyConfig.Headers,
        data: proxyConfig.Data,
        callback?: () => void,
      ): void
    }
  }

  namespace events {
    namespace type {
      type StaticPC =
        | 'app.record.index.show'
        | 'app.record.index.edit.show'
        | 'app.record.index.edit.submit'
        | 'app.record.index.edit.submit.success'
        | 'app.record.index.delete.submit'
        | 'app.record.detail.show'
        | 'app.record.detail.delete.submit'
        | 'app.record.detail.process.proceed'
        | 'app.record.create.show'
        | 'app.record.create.submit'
        | 'app.record.create.submit.success'
        | 'app.record.edit.show'
        | 'app.record.edit.submit'
        | 'app.record.edit.submit.success'
        | 'app.record.print.show'
        | 'app.report.show'
        | 'portal.show'
      type StaticMobile =
        | 'mobile.app.record.index.show'
        | 'mobile.app.record.detail.show'
        | 'mobile.app.record.detail.delete.submit'
        | 'mobile.app.record.detail.process.proceed'
        | 'mobile.app.record.create.show'
        | 'mobile.app.record.create.submit'
        | 'mobile.app.record.create.submit.success'
        | 'mobile.app.record.edit.show'
        | 'mobile.app.record.edit.submit'
        | 'mobile.app.record.edit.submit.success'
        | 'mobile.app.report.show'
        | 'mobile.portal.show'
      type DynamicPC<T extends string> =
        | `app.record.index.edit.change.${T}`
        | `app.record.create.change.${T}`
        | `app.record.edit.change.${T}`
      type DynamicMobile<T extends string> =
        | `mobile.app.record.create.change.${T}`
        | `mobile.app.record.edit.change.${T}`

      type all =
        | StaticPC
        | DynamicPC<string>
        | StaticMobile
        | DynamicMobile<string>
    }

    namespace response {
      namespace record {
        interface BeforeCreate {
          [key: string]:
            | fieldTypes.Calc
            | fieldTypes.CheckBox
            | fieldTypes.CreatedTime
            | fieldTypes.Creator
            | fieldTypes.Date
            | fieldTypes.DateTime
            | fieldTypes.DropDown
            | fieldTypes.File
            | fieldTypes.GroupSelect
            | fieldTypes.Link
            | fieldTypes.Modifier
            | fieldTypes.MultiLineText
            | fieldTypes.MultiSelect
            | fieldTypes.Number
            | fieldTypes.OrganizationSelect
            | fieldTypes.RadioButton
            | fieldTypes.RecordNumber
            | fieldTypes.RichText
            | fieldTypes.SingleLineText
            | fieldTypes.Time
            | fieldTypes.UpdatedTime
            | fieldTypes.UserSelect
            | fieldTypes.SubTable.Main
        }
        interface Created extends BeforCreate {
          $id: fieldTypes.Id
          $revision: fieldTypes.Revision
        }
      }
      type Common = {
        appId: number
        type: type.all
      }
      interface IndexShow extends Common {
        viewType: 'list' | 'calendar' | 'custom'
        viewId: number
        viewName: string
        records: record.Created[] | Record<string, record.Created[]>
        offset: number | null
        size: number | null
        date: string | null
      }
      interface Detail extends Common {
        /**
         * number:
         * - app.record.detail.show
         * - app.record.edit.show
         * - app.record.index.delete.submit
         * - app.record.edit.submit
         * - app.record.print.show
         * - app.record.detail.delete.submit
         * - app.record.edit.change.*
         * string:
         * - app.record.index.edit.show
         * - app.record.index.edit.submit
         * - app.record.edit.submit.success
         * - app.record.index.edit.change.*
         */
        recordId: number | string
        record: record.Created
      }
      interface Create extends Common {
        record: record.BeforeCreate
        reuse: boolean
      }
      interface Change extends Common {
        record: record.BeforeCreate | record.Created
        changes: {
          field: record.BeforeCreate
          row: null | fieldTypes.SubTable.Value
        }
      }

      type all = IndexShow | Detail | Create | Change // TODO 不足してたら足す
    }

    type ArgEventType = type.all | type.all[]
    type HandlerReturn = response.all | void | false // FIXME 多分大丈夫だけど要確認
    function on(
      event: ArgEventType,
      handler: (
        event: response.all,
      ) => HandlerReturn | kintone.Promise<HandlerReturn>,
    ): void
    function off(
      event: ArgEventType,
      handler: (
        event: response.all,
      ) => HandlerReturn | kintone.Promise<HandlerReturn>,
    ): boolean
    function off(event: ArgEventType): boolean
  }

  class Promise<T> {
    constructor(
      callback: (
        resolve: (resolved: T) => unknown,
        reject: (rejected: unknown) => unknown,
      ) => void,
    )

    then(callback: (resolved: T) => unknown): Promise<unknown>
    catch(callback: (rejected: unknown) => unknown): Promise<unknown>

    static resolve(resolved: T): Promise<unknown>
    static reject(rejected: unknown): Promise<unknown>
    static all(listOfPromise: Array<Promise<unknown>>): Promise<unknown>
  }

  namespace fieldTypes {
    interface SingleLineText {
      type?: 'SINGLE_LINE_TEXT'
      value: string
      disabled?: boolean
      error?: string
    }

    interface RichText {
      type?: 'RICH_TEXT'
      value: string
      disabled?: boolean
      error?: string
    }

    interface MultiLineText {
      type?: 'MULTI_LINE_TEXT'
      value: string
      disabled?: boolean
      error?: string
    }

    interface Number {
      type?: 'NUMBER'
      value: string
      disabled?: boolean
      error?: string
    }

    interface Calc {
      type: 'CALC'
      value: string
      disabled?: boolean
    }

    interface RadioButton {
      type?: 'RADIO_BUTTON'
      value: string
      disabled?: boolean
      error?: string
    }

    interface DropDown {
      type?: 'DROP_DOWN'
      value: string
      disabled?: boolean
      error?: string
    }

    interface Date {
      type?: 'DATE'
      value: string
      disabled?: boolean
      error?: string
    }

    interface Time {
      type?: 'TIME'
      value: string
      disabled?: boolean
      error?: string
    }

    interface DateTime {
      type?: 'DATETIME'
      value: string
      disabled?: boolean
      error?: string
    }

    interface Link {
      type?: 'LINK'
      value: string
      disabled?: boolean
      error?: string
    }

    interface CheckBox {
      type?: 'CHECK_BOX'
      value: string[]
      disabled?: boolean
      error?: string
    }

    interface MultiSelect {
      type?: 'MULTI_SELECT'
      value: string[]
      disabled?: boolean
      error?: string
    }

    interface UserSelect {
      type?: 'USER_SELECT'
      value: Array<{ code: string; name: string }>
      disabled?: boolean
      error?: string
    }

    interface OrganizationSelect {
      type?: 'ORGANIZATION_SELECT'
      value: Array<{ code: string; name: string }>
      disabled?: boolean
      error?: string
    }

    interface GroupSelect {
      type?: 'GROUP_SELECT'
      value: Array<{ code: string; name: string }>
      disabled?: boolean
      error?: string
    }

    interface File {
      type: 'FILE'
      value: Array<{
        contentType: string
        fileKey: string
        name: string
        size: string
      }>
      disabled?: boolean
      error?: string
    }

    interface Id {
      type: '__ID__'
      value: string
    }

    interface Revision {
      type: '__REVISION__'
      value: string
    }

    /**
     * field type of UserField is MODIFIER.
     * So error property not exists.
     */
    interface Modifier {
      type: 'MODIFIER'
      value: { code: string; name: string }
    }

    /**
     * field type of UserField is CREATOR.
     * So error property not exists.
     */
    interface Creator {
      type: 'CREATOR'
      value: { code: string; name: string }
    }

    interface RecordNumber {
      type: 'RECORD_NUMBER'
      value: string
      error?: string
    }

    interface UpdatedTime {
      type: 'UPDATED_TIME'
      value: string
      error?: string
    }

    interface CreatedTime {
      type: 'CREATED_TIME'
      value: string
      error?: string
    }

    namespace SubTable {
      interface Value {
        id: string | null
        value: {
          [key: string]:
            | SingleLineText
            | RichText
            | MultiLineText
            | fieldTypes.Number // Node: `Number`を直接使うと eslintで怒られる (ban-types)
            | Calc
            | RadioButton
            | DropDown
            | Date
            | Time
            | DateTime
            | Link
            | CheckBox
            | MultiSelect
            | UserSelect
            | OrganizationSelect
            | GroupSelect
            | File
        }
      }
      interface Main {
        type: 'SUBTABLE'
        value: Value
      }
    }
  }
}
@b4h0-c4t
Copy link

b4h0-c4t commented Mar 8, 2021

@rktyt
フィードバックありがとうございます。
TypeScript の型定義については今後洗練させていきたいと考えているため、記載いただいた内容も含めて検討します。

@zaki-yama zaki-yama added the pkg: dts-gen @kintone/dts-gen label Mar 26, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pkg: dts-gen @kintone/dts-gen
Projects
None yet
Development

No branches or pull requests

3 participants