From 15017ca714d3043b9884c25daedfb1ad4725db04 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Fri, 3 Jun 2022 12:17:49 +0200 Subject: [PATCH] chore(frontend): Fix all typescript errors (#10092) * fix dayjs * fix timeouts (we're not strictly speaking running in nodejs) * export unexported type * consolidate on a single FormInstance * no need to rename * fuse * forminstance 2 * locationChanged * BuiltLogic * remove Type.ts exception * fix duh * lay off the bin/check-typescript/strict script * don't think this is ever used or useful * no real need to hide the output * make typescript:check do what the name says * we're already strict Co-authored-by: Michael Matloka --- .github/workflows/ci-frontend.yml | 5 ++-- .github/workflows/ci-plugin-server.yml | 2 +- bin/check-typescript-strict | 23 --------------- .../Annotations/annotationsLogic.ts | 6 ++-- .../saveToDashboardModalLogic.ts | 13 ++++++--- frontend/src/lib/dayjs.ts | 29 ++++++++++++++++++- frontend/src/lib/utils/eventUsageLogic.ts | 10 +++---- .../src/scenes/cohorts/cohortEditLogic.ts | 6 ++-- .../confirmOrganizationLogic.ts | 2 +- .../organization/Settings/inviteLogic.ts | 3 +- .../edit/interface-jobs/interfaceJobsLogic.ts | 14 ++++----- frontend/src/scenes/plugins/pluginsLogic.ts | 2 +- frontend/src/scenes/plugins/utils.ts | 2 +- frontend/src/scenes/sceneLogic.ts | 7 +++-- .../session-recordings/player/seekbarLogic.ts | 6 ++-- package.json | 3 +- 16 files changed, 71 insertions(+), 62 deletions(-) delete mode 100755 bin/check-typescript-strict diff --git a/.github/workflows/ci-frontend.yml b/.github/workflows/ci-frontend.yml index 38677139527..bcd2cba7470 100644 --- a/.github/workflows/ci-frontend.yml +++ b/.github/workflows/ci-frontend.yml @@ -34,9 +34,8 @@ jobs: - name: Lint with ESLint run: yarn eslint - - name: Run typescript with strict - run: | - ./bin/check-typescript-strict + - name: Generate logic types and run typescript with strict + run: yarn typegen:write && yarn typescript:check jest-setup: # Split the tests into multiple chunks diff --git a/.github/workflows/ci-plugin-server.yml b/.github/workflows/ci-plugin-server.yml index 1214ec1dc2c..c8f1d88bbe1 100644 --- a/.github/workflows/ci-plugin-server.yml +++ b/.github/workflows/ci-plugin-server.yml @@ -128,4 +128,4 @@ jobs: # Below DB name has `test_` prepended, as that's how Django (ran above) creates the test DB DATABASE_URL: 'postgres://postgres:postgres@localhost:5432/test_posthog' REDIS_URL: 'redis://localhost' - run: cd plugin-server && yarn typescript:check && yarn test:${{matrix.group}} + run: cd plugin-server && yarn test:${{matrix.group}} diff --git a/bin/check-typescript-strict b/bin/check-typescript-strict deleted file mode 100755 index 764eb0a79a7..00000000000 --- a/bin/check-typescript-strict +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -echo "Refreshing Kea logic types and running the TypeScript compiler in dry mode..." -echo - -IGNORED_SPECS=('Type.ts') # *Type.ts* files are ignored, as they are generally created by kea-typegen - -yarn typegen:write &> /dev/null - -ALL_ERRORS=$(yarn typescript:check --strict 2> /dev/null | grep error | grep frontend) -ERRORS_COUNT=$(echo "${ALL_ERRORS}" | wc -l) -NEW_ERRORS=$(echo "${ALL_ERRORS}" | grep --invert-match --fixed-strings ${IGNORED_SPECS}) -NEW_ERRORS_COUNT="$(echo "${NEW_ERRORS}" | wc -l)" - -if test -z "${NEW_ERRORS}" -then - echo "No TypeScript errors found in this PR! 🚀" - exit 0 -else - echo "Found ${NEW_ERRORS_COUNT} TypeScript errors in this PR! 💥" - echo "${NEW_ERRORS}" - exit 1 -fi diff --git a/frontend/src/lib/components/Annotations/annotationsLogic.ts b/frontend/src/lib/components/Annotations/annotationsLogic.ts index e2c00ab30f8..6f4a07e9635 100644 --- a/frontend/src/lib/components/Annotations/annotationsLogic.ts +++ b/frontend/src/lib/components/Annotations/annotationsLogic.ts @@ -1,6 +1,6 @@ import { kea } from 'kea' import api from 'lib/api' -import { dayjs, now } from 'lib/dayjs' +import { dayjs, now, OpUnitType } from 'lib/dayjs' import { deleteWithUndo, determineDifferenceType, groupBy, toParams } from '~/lib/utils' import { annotationsModel } from '~/models/annotationsModel' import { getNextKey } from './utils' @@ -29,7 +29,7 @@ export const annotationsLogic = kea({ }), deleteAnnotation: (id: string) => ({ id }), updateDiffType: (dates: string[]) => ({ dates }), - setDiffType: (type: dayjs.OpUnitType) => ({ type }), + setDiffType: (type: OpUnitType) => ({ type }), }), loaders: ({ props }) => ({ annotations: { @@ -90,7 +90,7 @@ export const annotationsLogic = kea({ (annotationsList, diffType) => groupBy(annotationsList, (annotation) => dayjs(annotation['date_marker']) - .startOf(diffType as dayjs.OpUnitType) + .startOf(diffType as OpUnitType) .format('YYYY-MM-DD') ), ], diff --git a/frontend/src/lib/components/SaveToDashboard/saveToDashboardModalLogic.ts b/frontend/src/lib/components/SaveToDashboard/saveToDashboardModalLogic.ts index 4e1fa2914c5..e65d02ecc4e 100644 --- a/frontend/src/lib/components/SaveToDashboard/saveToDashboardModalLogic.ts +++ b/frontend/src/lib/components/SaveToDashboard/saveToDashboardModalLogic.ts @@ -5,7 +5,7 @@ import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import type { saveToDashboardModalLogicType } from './saveToDashboardModalLogicType' import { newDashboardLogic } from 'scenes/dashboard/newDashboardLogic' import { DashboardType, InsightModel, InsightType } from '~/types' -import Fuse from 'fuse.js' +import FuseClass from 'fuse.js' import { lemonToast } from 'lib/components/lemonToast' import { router } from 'kea-router' import { urls } from 'scenes/urls' @@ -15,6 +15,11 @@ export interface SaveToDashboardModalLogicProps { insight: Partial fromDashboard?: number } + +// Helping kea-typegen navigate the exported default class for Fuse +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface Fuse extends FuseClass {} + export const saveToDashboardModalLogic = kea({ path: ['lib', 'components', 'SaveToDashboard', 'saveToDashboardModalLogic'], props: {} as SaveToDashboardModalLogicProps, @@ -71,8 +76,8 @@ export const saveToDashboardModalLogic = kea({ ], dashboardsFuse: [ () => [dashboardsModel.selectors.nameSortedDashboards], - (nameSortedDashboards) => { - return new Fuse(nameSortedDashboards || [], { + (nameSortedDashboards): Fuse => { + return new FuseClass(nameSortedDashboards || [], { keys: ['name', 'description', 'tags'], threshold: 0.3, }) @@ -82,7 +87,7 @@ export const saveToDashboardModalLogic = kea({ (s) => [s.searchQuery, s.dashboardsFuse, dashboardsModel.selectors.nameSortedDashboards], (searchQuery, dashboardsFuse, nameSortedDashboards): DashboardType[] => searchQuery.length - ? dashboardsFuse.search(searchQuery).map((r: Fuse.FuseResult) => r.item) + ? dashboardsFuse.search(searchQuery).map((r: FuseClass.FuseResult) => r.item) : nameSortedDashboards, ], currentDashboards: [ diff --git a/frontend/src/lib/dayjs.ts b/frontend/src/lib/dayjs.ts index bb48a61bd7f..952c960c745 100644 --- a/frontend/src/lib/dayjs.ts +++ b/frontend/src/lib/dayjs.ts @@ -1,5 +1,5 @@ // eslint-disable-next-line no-restricted-imports -import dayjs, { Dayjs } from 'dayjs' +import dayjs, { Dayjs as DayjsOriginal } from 'dayjs' import LocalizedFormat from 'dayjs/plugin/localizedFormat' import relativeTime from 'dayjs/plugin/relativeTime' import isSameOrAfter from 'dayjs/plugin/isSameOrAfter' @@ -20,3 +20,30 @@ dayjs.extend(duration) const now = (): Dayjs => dayjs() export { dayjs, now } + +// The lines below are copied from "node_modules/dayjs/index.ts" to help typescript and typegen. +// We could only use types like "dayjs.OpUnitType", causing errors such as: +// error TS2312: An interface can only extend an object type or intersection of object types with statically known members. + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface Dayjs extends DayjsOriginal {} + +export type UnitTypeShort = 'd' | 'M' | 'y' | 'h' | 'm' | 's' | 'ms' + +export type UnitTypeLong = 'millisecond' | 'second' | 'minute' | 'hour' | 'day' | 'month' | 'year' | 'date' + +export type UnitTypeLongPlural = + | 'milliseconds' + | 'seconds' + | 'minutes' + | 'hours' + | 'days' + | 'months' + | 'years' + | 'dates' + +export type UnitType = UnitTypeLong | UnitTypeLongPlural | UnitTypeShort + +export type OpUnitType = UnitType | 'week' | 'weeks' | 'w' +export type QUnitType = UnitType | 'quarter' | 'quarters' | 'Q' +export type ManipulateType = Omit diff --git a/frontend/src/lib/utils/eventUsageLogic.ts b/frontend/src/lib/utils/eventUsageLogic.ts index 9679f5acfba..649a7dd2399 100644 --- a/frontend/src/lib/utils/eventUsageLogic.ts +++ b/frontend/src/lib/utils/eventUsageLogic.ts @@ -24,7 +24,7 @@ import { FilterLogicalOperator, PropertyFilterValue, } from '~/types' -import { dayjs } from 'lib/dayjs' +import type { Dayjs } from 'lib/dayjs' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' import type { PersonsModalParams } from 'scenes/trends/personsModalLogic' import { EventIndex } from '@posthog/react-rrweb-player' @@ -267,9 +267,9 @@ export const eventUsageLogic = kea({ delay, }), reportDashboardModeToggled: (mode: DashboardMode, source: DashboardEventSource | null) => ({ mode, source }), - reportDashboardRefreshed: (lastRefreshed?: string | dayjs.Dayjs | null) => ({ lastRefreshed }), + reportDashboardRefreshed: (lastRefreshed?: string | Dayjs | null) => ({ lastRefreshed }), reportDashboardItemRefreshed: (dashboardItem: InsightModel) => ({ dashboardItem }), - reportDashboardDateRangeChanged: (dateFrom?: string | dayjs.Dayjs, dateTo?: string | dayjs.Dayjs | null) => ({ + reportDashboardDateRangeChanged: (dateFrom?: string | Dayjs, dateTo?: string | Dayjs | null) => ({ dateFrom, dateTo, }), @@ -369,10 +369,10 @@ export const eventUsageLogic = kea({ reportExperimentArchived: (experiment: Experiment) => ({ experiment }), reportExperimentCreated: (experiment: Experiment) => ({ experiment }), reportExperimentViewed: (experiment: Experiment) => ({ experiment }), - reportExperimentLaunched: (experiment: Experiment, launchDate: dayjs.Dayjs) => ({ experiment, launchDate }), + reportExperimentLaunched: (experiment: Experiment, launchDate: Dayjs) => ({ experiment, launchDate }), reportExperimentCompleted: ( experiment: Experiment, - endDate: dayjs.Dayjs, + endDate: Dayjs, duration: number, significant: boolean ) => ({ diff --git a/frontend/src/scenes/cohorts/cohortEditLogic.ts b/frontend/src/scenes/cohorts/cohortEditLogic.ts index 477c6f703a7..0f6a489524e 100644 --- a/frontend/src/scenes/cohorts/cohortEditLogic.ts +++ b/frontend/src/scenes/cohorts/cohortEditLogic.ts @@ -42,7 +42,7 @@ export const cohortEditLogic = kea([ deleteCohort: true, fetchCohort: (id: CohortType['id']) => ({ id }), onCriteriaChange: (newGroup: Partial, id: string) => ({ newGroup, id }), - setPollTimeout: (pollTimeout: NodeJS.Timeout | null) => ({ pollTimeout }), + setPollTimeout: (pollTimeout: number | null) => ({ pollTimeout }), checkIfFinishedCalculating: (cohort: CohortType) => ({ cohort }), setOuterGroupsType: (type: FilterLogicalOperator) => ({ type }), @@ -138,7 +138,7 @@ export const cohortEditLogic = kea([ }, ], pollTimeout: [ - null as NodeJS.Timeout | null, + null as number | null, { setPollTimeout: (_, { pollTimeout }) => pollTimeout, }, @@ -262,7 +262,7 @@ export const cohortEditLogic = kea([ checkIfFinishedCalculating: async ({ cohort }, breakpoint) => { if (cohort.is_calculating) { actions.setPollTimeout( - setTimeout(async () => { + window.setTimeout(async () => { const newCohort = await api.cohorts.get(cohort.id) breakpoint() actions.checkIfFinishedCalculating(newCohort) diff --git a/frontend/src/scenes/organization/ConfirmOrganization/confirmOrganizationLogic.ts b/frontend/src/scenes/organization/ConfirmOrganization/confirmOrganizationLogic.ts index 97fe4a21573..5275f771f36 100644 --- a/frontend/src/scenes/organization/ConfirmOrganization/confirmOrganizationLogic.ts +++ b/frontend/src/scenes/organization/ConfirmOrganization/confirmOrganizationLogic.ts @@ -7,7 +7,7 @@ import type { confirmOrganizationLogicType } from './confirmOrganizationLogicTyp import { forms } from 'kea-forms' import { lemonToast } from 'lib/components/lemonToast' -interface ConfirmOrganizationFormValues { +export interface ConfirmOrganizationFormValues { organization_name?: string first_name?: string } diff --git a/frontend/src/scenes/organization/Settings/inviteLogic.ts b/frontend/src/scenes/organization/Settings/inviteLogic.ts index b58cb9a0eaa..f9038b106fe 100644 --- a/frontend/src/scenes/organization/Settings/inviteLogic.ts +++ b/frontend/src/scenes/organization/Settings/inviteLogic.ts @@ -31,6 +31,7 @@ export const inviteLogic = kea({ }, connect: { values: [preflightLogic, ['preflight']], + actions: [router, ['locationChanged']], }, reducers: () => ({ isInviteModalShown: [ @@ -38,7 +39,7 @@ export const inviteLogic = kea({ { showInviteModal: () => true, hideInviteModal: () => false, - [router.actionTypes.locationChanged]: () => false, + locationChanged: () => false, }, ], invitesToSend: [ diff --git a/frontend/src/scenes/plugins/edit/interface-jobs/interfaceJobsLogic.ts b/frontend/src/scenes/plugins/edit/interface-jobs/interfaceJobsLogic.ts index f0445059b46..f2879dcf8f9 100644 --- a/frontend/src/scenes/plugins/edit/interface-jobs/interfaceJobsLogic.ts +++ b/frontend/src/scenes/plugins/edit/interface-jobs/interfaceJobsLogic.ts @@ -1,7 +1,7 @@ -import { FormInstance } from 'antd' +import type { FormInstance } from 'antd/lib/form/hooks/useForm.d' import { kea } from 'kea' import api from 'lib/api' -import { interfaceJobsLogicType } from './interfaceJobsLogicType' +import type { interfaceJobsLogicType } from './interfaceJobsLogicType' import { pluginsLogic } from 'scenes/plugins/pluginsLogic' import { JobSpec } from '~/types' import { lemonToast } from 'lib/components/lemonToast' @@ -25,7 +25,7 @@ export const interfaceJobsLogic = kea({ setRunJobAvailable: (isAvailable: boolean) => ({ isAvailable }), runJob: (form: FormInstance) => ({ form }), playButtonOnClick: (form: FormInstance, jobHasEmptyPayload: boolean) => ({ form, jobHasEmptyPayload }), - setRunJobAvailableTimeout: (timeout: NodeJS.Timeout) => ({ timeout }), + setRunJobAvailableTimeout: (timeout: number) => ({ timeout }), }, reducers: { isJobModalOpen: [ @@ -41,7 +41,7 @@ export const interfaceJobsLogic = kea({ }, ], runJobAvailableTimeout: [ - null as NodeJS.Timeout | null, + null as number | null, { setRunJobAvailableTimeout: (_, { timeout }) => timeout, }, @@ -87,10 +87,10 @@ export const interfaceJobsLogic = kea({ if (values.runJobAvailableTimeout) { clearTimeout(values.runJobAvailableTimeout) } - setTimeout(() => { - const timeout = actions.setRunJobAvailable(true) - actions.setRunJobAvailableTimeout(timeout) + const timeout = window.setTimeout(() => { + actions.setRunJobAvailable(true) }, 15000) + actions.setRunJobAvailableTimeout(timeout) lemonToast.success('Job has been enqueued') }, diff --git a/frontend/src/scenes/plugins/pluginsLogic.ts b/frontend/src/scenes/plugins/pluginsLogic.ts index fb312117432..5bc41cbb114 100644 --- a/frontend/src/scenes/plugins/pluginsLogic.ts +++ b/frontend/src/scenes/plugins/pluginsLogic.ts @@ -14,7 +14,7 @@ import { import { userLogic } from 'scenes/userLogic' import { getConfigSchemaArray, getConfigSchemaObject, getPluginConfigFormData } from 'scenes/plugins/utils' import posthog from 'posthog-js' -import { FormInstance } from 'antd/lib/form' +import type { FormInstance } from 'antd/lib/form/hooks/useForm.d' import { canGloballyManagePlugins, canInstallPlugins } from './access' import { teamLogic } from '../teamLogic' import { createDefaultPluginSource } from 'scenes/plugins/source/createDefaultPluginSource' diff --git a/frontend/src/scenes/plugins/utils.ts b/frontend/src/scenes/plugins/utils.ts index ddd328007b9..500916dbcea 100644 --- a/frontend/src/scenes/plugins/utils.ts +++ b/frontend/src/scenes/plugins/utils.ts @@ -1,5 +1,5 @@ import { PluginConfigSchema } from '@posthog/plugin-scaffold' -import { FormInstance } from 'antd' +import type { FormInstance } from 'antd/lib/form/hooks/useForm.d' import { PluginTypeWithConfig } from 'scenes/plugins/types' // Keep this in sync with: posthog/api/plugin.py diff --git a/frontend/src/scenes/sceneLogic.ts b/frontend/src/scenes/sceneLogic.ts index 631dd9abfdf..0d379475756 100644 --- a/frontend/src/scenes/sceneLogic.ts +++ b/frontend/src/scenes/sceneLogic.ts @@ -1,4 +1,4 @@ -import { kea } from 'kea' +import { BuiltLogic, kea } from 'kea' import { router } from 'kea-router' import posthog from 'posthog-js' import type { sceneLogicType } from './sceneLogicType' @@ -38,6 +38,7 @@ export const sceneLogic = kea({ connect: () => ({ logic: [router, userLogic, preflightLogic], values: [featureFlagLogic, ['featureFlags']], + actions: [router, ['locationChanged']], }), path: ['scenes', 'sceneLogic'], actions: { @@ -145,7 +146,7 @@ export const sceneLogic = kea({ ], activeSceneLogic: [ (s) => [s.activeLoadedScene, s.sceneParams], - (activeLoadedScene, sceneParams) => + (activeLoadedScene, sceneParams): BuiltLogic | null => activeLoadedScene?.logic ? activeLoadedScene.logic.build(activeLoadedScene.paramsToProps?.(sceneParams) || {}) : null, @@ -376,7 +377,7 @@ export const sceneLogic = kea({ reloadBrowserDueToImportError: () => { window.location.reload() }, - [router.actionTypes.locationChanged]: () => { + locationChanged: () => { // Remove trailing slash const { location: { pathname, search, hash }, diff --git a/frontend/src/scenes/session-recordings/player/seekbarLogic.ts b/frontend/src/scenes/session-recordings/player/seekbarLogic.ts index 5f457782e54..7a777e6ae85 100644 --- a/frontend/src/scenes/session-recordings/player/seekbarLogic.ts +++ b/frontend/src/scenes/session-recordings/player/seekbarLogic.ts @@ -1,4 +1,4 @@ -import { MutableRefObject as ReactMutableRefObject } from 'react' +import { MutableRefObject } from 'react' import { kea } from 'kea' import type { seekbarLogicType } from './seekbarLogicType' import { sessionRecordingPlayerLogic } from 'scenes/session-recordings/player/sessionRecordingPlayerLogic' @@ -36,8 +36,8 @@ export const seekbarLogic = kea({ handleDown: (event: ReactInteractEvent) => ({ event }), handleClick: (event: ReactInteractEvent) => ({ event }), handleTickClick: (playerPosition: PlayerPosition) => ({ playerPosition }), - setSlider: (ref: ReactMutableRefObject) => ({ ref }), - setThumb: (ref: ReactMutableRefObject) => ({ ref }), + setSlider: (ref: MutableRefObject) => ({ ref }), + setThumb: (ref: MutableRefObject) => ({ ref }), debouncedSetTime: (time: number) => ({ time }), endSeeking: true, }, diff --git a/package.json b/package.json index c49a169b648..d30b63e4751 100644 --- a/package.json +++ b/package.json @@ -37,8 +37,7 @@ "build:esbuild": "node frontend/build.mjs", "prettier": "prettier --write \"./**/*.{js,mjs,ts,tsx,json,yaml,yml,css,scss}\"", "prettier:check": "prettier --check \"./**/*.{js,mjs,ts,tsx,json,yaml,yml,css,scss}\"", - "typescript:check": "tsc", - "typescript:clean": "tsc --build --clean", + "typescript:check": "tsc --noEmit && echo \"No errors reported by tsc.\"", "eslint": "eslint frontend/src", "typegen:write": "kea-typegen write", "typegen:check": "kea-typegen check",