Skip to content

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

@rktyt

Description

@rktyt

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
      }
    }
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions