enable typechecked rules, switch them to warn

This commit is contained in:
Samuel Newman
2026-01-15 15:24:52 +02:00
parent d0597f6a40
commit 6a0be039e9
20 changed files with 105 additions and 44 deletions
+34 -9
View File
@@ -1,9 +1,12 @@
// @ts-check
import js from '@eslint/js'
import tseslint from 'typescript-eslint'
import { defineConfig } from 'eslint/config';
import react from 'eslint-plugin-react'
import reactHooks from 'eslint-plugin-react-hooks'
// @ts-expect-error no types
import reactNative from 'eslint-plugin-react-native'
// @ts-expect-error no types
import reactNativeA11y from 'eslint-plugin-react-native-a11y'
import simpleImportSort from 'eslint-plugin-simple-import-sort'
import importX from 'eslint-plugin-import-x'
@@ -11,8 +14,9 @@ import lingui from 'eslint-plugin-lingui'
import reactCompiler from 'eslint-plugin-react-compiler'
import bskyInternal from 'eslint-plugin-bsky-internal'
import globals from 'globals'
import tsParser from '@typescript-eslint/parser'
export default tseslint.config(
export default defineConfig(
// Global ignores
{
ignores: [
@@ -40,18 +44,25 @@ export default tseslint.config(
js.configs.recommended,
// TypeScript rules
...tseslint.configs.recommended,
tseslint.configs.recommendedTypeChecked,
// React Hooks rules
reactHooks.configs.flat.recommended,
// Import X
// @ts-expect-error https://github.com/un-ts/eslint-plugin-import-x/issues/439
importX.flatConfigs.recommended,
importX.flatConfigs.typescript,
importX.flatConfigs['react-native'],
// Main configuration for all JS/TS/JSX/TSX files
{
files: ['**/*.{js,jsx,ts,tsx}'],
plugins: {
react,
'react-hooks': reactHooks,
'react-native': reactNative,
'react-native-a11y': reactNativeA11y,
'simple-import-sort': simpleImportSort,
'import-x': importX,
lingui,
'react-compiler': reactCompiler,
'bsky-internal': bskyInternal,
@@ -65,6 +76,8 @@ export default tseslint.config(
...globals.es2021,
},
parserOptions: {
parser: tsParser,
projectService: true,
ecmaFeatures: {
jsx: true,
},
@@ -83,8 +96,6 @@ export default tseslint.config(
'react/no-unescaped-entities': 'off',
'react/prop-types': 'off',
// React Hooks rules
...reactHooks.configs.recommended.rules,
// React Native rules
'react-native/no-inline-styles': 'off',
@@ -188,13 +199,27 @@ export default tseslint.config(
],
'@typescript-eslint/no-require-imports': 'off',
// Maintain previous behavior - these are stricter in typescript-eslint v8
// `warn` ones are probably worth fixing. `off` ones are a bit too nit-picky
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-empty-object-type': 'off',
'@typescript-eslint/no-unused-expressions': 'off',
'@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
'@typescript-eslint/no-wrapper-object-types': 'off',
'@typescript-eslint/no-unsafe-function-type': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off',
'@typescript-eslint/unbound-method': 'off',
'@typescript-eslint/no-unsafe-argument': 'off',
'@typescript-eslint/no-unsafe-return': 'off',
'@typescript-eslint/no-unsafe-member-access': 'warn',
'@typescript-eslint/no-unsafe-call': 'warn',
'@typescript-eslint/no-floating-promises': 'warn',
'@typescript-eslint/no-misused-promises': 'warn',
'@typescript-eslint/require-await': 'warn',
'@typescript-eslint/no-unsafe-enum-comparison': 'warn',
'@typescript-eslint/no-unnecessary-type-assertion': 'warn',
'@typescript-eslint/no-redundant-type-constituents': 'warn',
'@typescript-eslint/no-duplicate-type-constituents': 'warn',
'@typescript-eslint/no-base-to-string': 'warn',
'@typescript-eslint/prefer-promise-reject-errors': 'warn',
'@typescript-eslint/await-thenable': 'warn',
// Import rules
'import-x/consistent-type-specifier-style': ['warn', 'prefer-inline'],
+1
View File
@@ -255,6 +255,7 @@
"babel-plugin-react-compiler": "^19.1.0-rc.3",
"babel-preset-expo": "~54.0.0",
"eslint": "^9.39.2",
"eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-bsky-internal": "link:./eslint",
"eslint-plugin-import-x": "^4.16.1",
"eslint-plugin-lingui": "^0.11.0",
+2 -3
View File
@@ -80,9 +80,8 @@ export function Outer({
)
const handleBackgroundPress = React.useCallback(
async (e: GestureResponderEvent) => {
webOptions?.onBackgroundPress ? webOptions.onBackgroundPress(e) : close()
},
async (e: GestureResponderEvent) =>
webOptions?.onBackgroundPress ? webOptions.onBackgroundPress(e) : close(),
[webOptions, close],
)
+1
View File
@@ -35,6 +35,7 @@ export function Text({
if (__DEV__) {
if (!emoji && childHasEmoji(children)) {
logger.warn(
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions, @typescript-eslint/no-base-to-string
`Text: emoji detected but emoji not enabled: "${children}"\n\nPlease add <Text emoji />'`,
)
}
+1 -1
View File
@@ -98,7 +98,7 @@ function DialogInner({
} = useRemoveLiveStatusMutation()
const {minutesUntilExpiry, expiryDateTime} = useMemo(() => {
tick!
void tick
const expiry = new Date(status.expiresAt ?? new Date())
return {
+6 -3
View File
@@ -13,7 +13,11 @@ import {Admonition} from '#/components/Admonition'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import * as TextField from '#/components/forms/TextField'
import {getLiveServiceNames} from '#/components/live/utils'
import {
displayDuration,
getLiveServiceNames,
useDebouncedValue,
} from '#/components/live/utils'
import {Loader} from '#/components/Loader'
import * as ProfileCard from '#/components/ProfileCard'
import * as Select from '#/components/Select'
@@ -21,7 +25,6 @@ import {Text} from '#/components/Typography'
import type * as bsky from '#/types/bsky'
import {LinkPreview} from './LinkPreview'
import {useLiveLinkMetaQuery, useUpsertLiveStatusMutation} from './queries'
import {displayDuration, useDebouncedValue} from './utils'
export function GoLiveDialog({
control,
@@ -57,7 +60,7 @@ function DialogInner({profile}: {profile: bsky.profile.AnyProfileView}) {
const time = useCallback(
(offset: number) => {
tick!
void tick
const date = new Date()
date.setMinutes(date.getMinutes() + offset)
@@ -211,8 +211,8 @@ function Inner(props: ReportDialogProps) {
logger.metric(
'reportDialog:success',
{
reason: state.selectedOption?.reason!,
labeler: state.selectedLabeler?.creator.handle!,
reason: state.selectedOption?.reason ?? '',
labeler: state.selectedLabeler?.creator.handle ?? '',
details: !!state.details,
},
{statsig: false},
@@ -9,7 +9,7 @@ import {Admonition} from '#/components/Admonition'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import {Loader} from '#/components/Loader'
import * as toast from '#/components/Toast'
import * as Toast from '#/components/Toast'
import {Span, Text} from '#/components/Typography'
import {useUpdateLiveEventPreferences} from '#/features/liveEvents/preferences'
import {
@@ -61,14 +61,14 @@ function Inner({
feed,
metricContext,
onUpdateSuccess({undoAction}) {
toast.show(
<toast.Outer>
<toast.Icon />
<toast.Text>
Toast.show(
<Toast.Outer>
<Toast.Icon />
<Toast.Text>
<Trans>Your live event preferences have been updated.</Trans>
</toast.Text>
</Toast.Text>
{undoAction && (
<toast.Action
<Toast.Action
label={_(msg`Undo`)}
onPress={() => {
if (undoAction) {
@@ -76,12 +76,10 @@ function Inner({
}
}}>
<Trans>Undo</Trans>
</toast.Action>
</Toast.Action>
)}
</toast.Outer>,
{
type: 'success',
},
</Toast.Outer>,
{type: 'success'},
)
/*
+1 -1
View File
@@ -128,7 +128,7 @@ export function useGeolocationServiceResponse() {
useEffect(() => {
return onGeolocationServiceResponseUpdate(config => {
setConfig(config!)
setConfig(config)
})
}, [])
+1 -1
View File
@@ -17,7 +17,7 @@ export function useActorStatus(actor?: bsky.profile.AnyProfileView) {
const config = useLiveNowConfig()
return useMemo(() => {
tick! // revalidate every minute
void tick // revalidate every minute
if (shadowed && 'status' in shadowed && shadowed.status) {
const isValid = validateStatus(shadowed.status, config)
+1 -1
View File
@@ -67,7 +67,7 @@ export function isPlainArray(value: unknown) {
}
// Copied from: https://github.com/jonschlinkert/is-plain-object
export function isPlainObject(o: any): o is Object {
export function isPlainObject(o: any): o is object {
if (!hasObjectPrototype(o)) {
return false
}
+1 -1
View File
@@ -95,7 +95,7 @@ export class Convo {
this.convoId = params.convoId
this.agent = params.agent
this.events = params.events
this.senderUserDid = params.agent.session?.did!
this.senderUserDid = params.agent.assertDid
if (params.placeholderData) {
this.setupPlaceholderData(params.placeholderData)
+5
View File
@@ -22,6 +22,8 @@ const UPDATE_EVENT = 'BSKY_UPDATE'
let _state: Schema = defaults
const _emitter = new EventEmitter()
// async, to match native implementation
// eslint-disable-next-line @typescript-eslint/require-await
export async function init() {
broadcast.onmessage = onBroadcastMessage
window.onstorage = onStorage
@@ -37,6 +39,7 @@ export function get<K extends keyof Schema>(key: K): Schema[K] {
}
get satisfies PersistedApi['get']
// eslint-disable-next-line @typescript-eslint/require-await
export async function write<K extends keyof Schema>(
key: K,
value: Schema[K],
@@ -82,6 +85,7 @@ export function onUpdate<K extends keyof Schema>(
}
onUpdate satisfies PersistedApi['onUpdate']
// eslint-disable-next-line @typescript-eslint/require-await
export async function clearStorage() {
try {
localStorage.removeItem(BSKY_STORAGE)
@@ -102,6 +106,7 @@ function onStorage() {
}
}
// eslint-disable-next-line @typescript-eslint/require-await
async function onBroadcastMessage({data}: MessageEvent) {
if (
typeof data === 'object' &&
+1 -1
View File
@@ -23,7 +23,7 @@ export {
useExternalEmbedsPrefs,
useSetExternalEmbedPref,
} from './external-embeds-prefs'
export * from './hidden-posts'
export {useHiddenPosts, useHiddenPostsApi} from './hidden-posts'
export {useLabelDefinitions} from './label-defs'
export {useLanguagePrefs, useLanguagePrefsApi} from './languages'
export {useSetSubtitlesEnabled, useSubtitlesEnabled} from './subtitles'
+7 -1
View File
@@ -15,6 +15,12 @@ export type UsePreferencesQueryResponse = Omit<
}
export type ThreadViewPreferences = {
sort: 'hotness' | 'oldest' | 'newest' | 'most-likes' | 'random' | string
sort:
| 'hotness'
| 'oldest'
| 'newest'
| 'most-likes'
| 'random'
| (string & {})
lab_treeViewEnabled?: boolean
}
+1 -1
View File
@@ -26,7 +26,7 @@ type Controls = {
/**
* The did of the account to populate the login form with.
*/
requestedAccount?: string | 'none' | 'new' | 'starterpack'
requestedAccount?: (string & {}) | 'none' | 'new' | 'starterpack'
}) => void
/**
* Clears the requested account so that next time the logged out view is
@@ -14,7 +14,9 @@ import {
import {AppBskyRichtextFacet, RichText} from '@atproto/api'
import PasteInput, {
type PastedFile,
type PasteInputRef, // @ts-expect-error no types when installing from github
type PasteInputRef,
// @ts-expect-error no types when installing from github
// eslint-disable-next-line import-x/no-unresolved
} from '@mattermost/react-native-paste-input'
import {POST_IMG_MAX} from '#/lib/constants'
+2 -2
View File
@@ -153,9 +153,9 @@ export function Button({
async (event: GestureResponderEvent) => {
event.stopPropagation()
event.preventDefault()
withLoading && setIsLoading(true)
if (withLoading) setIsLoading(true)
await onPress?.(event)
withLoading && setIsLoading(false)
if (withLoading) setIsLoading(false)
},
[onPress, withLoading],
)
+1
View File
@@ -51,6 +51,7 @@ function Text_DEPRECATED({
if (__DEV__) {
if (!emoji && childHasEmoji(children)) {
logger.warn(
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions, @typescript-eslint/no-base-to-string
`Text: emoji detected but emoji not enabled: "${children}"\n\nPlease add <Text emoji />'`,
)
}
+24 -4
View File
@@ -11176,7 +11176,7 @@ eslint-config-prettier@^8.5.0:
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz#3a06a662130807e2502fc3ff8b4143d8a0658e11"
integrity sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==
eslint-import-context@^0.1.9:
eslint-import-context@^0.1.8, eslint-import-context@^0.1.9:
version "0.1.9"
resolved "https://registry.yarnpkg.com/eslint-import-context/-/eslint-import-context-0.1.9.tgz#967b0b2f0a90ef4b689125e088f790f0b7756dbe"
integrity sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg==
@@ -11184,6 +11184,19 @@ eslint-import-context@^0.1.9:
get-tsconfig "^4.10.1"
stable-hash-x "^0.2.0"
eslint-import-resolver-typescript@^4.4.4:
version "4.4.4"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.4.4.tgz#3e83a9c25f4a053fe20e1b07b47e04e8519a8720"
integrity sha512-1iM2zeBvrYmUNTj2vSC/90JTHDth+dfOfiNKkxApWRsTJYNrc8rOdxxIf5vazX+BiAXTeOT0UvWpGI/7qIWQOw==
dependencies:
debug "^4.4.1"
eslint-import-context "^0.1.8"
get-tsconfig "^4.10.1"
is-bun-module "^2.0.0"
stable-hash-x "^0.2.0"
tinyglobby "^0.2.14"
unrs-resolver "^1.7.11"
"eslint-plugin-bsky-internal@link:./eslint":
version "0.0.0"
uid ""
@@ -13321,6 +13334,13 @@ is-boolean-object@^1.2.1:
call-bound "^1.0.3"
has-tostringtag "^1.0.2"
is-bun-module@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-bun-module/-/is-bun-module-2.0.0.tgz#4d7859a87c0fcac950c95e666730e745eae8bddd"
integrity sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==
dependencies:
semver "^7.7.1"
is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7:
version "1.2.7"
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055"
@@ -18141,7 +18161,7 @@ semver@^7.6.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13"
integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==
semver@^7.7.2, semver@^7.7.3:
semver@^7.7.1, semver@^7.7.2, semver@^7.7.3:
version "7.7.3"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946"
integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==
@@ -19291,7 +19311,7 @@ thunky@^1.0.2:
resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d"
integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==
tinyglobby@^0.2.11, tinyglobby@^0.2.15:
tinyglobby@^0.2.11, tinyglobby@^0.2.14, tinyglobby@^0.2.15:
version "0.2.15"
resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2"
integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==
@@ -19777,7 +19797,7 @@ unraw@^3.0.0:
resolved "https://registry.yarnpkg.com/unraw/-/unraw-3.0.0.tgz#73443ed70d2ab09ccbac2b00525602d5991fbbe3"
integrity sha512-08/DA66UF65OlpUDIQtbJyrqTR0jTAlJ+jsnkQ4jxR7+K5g5YG1APZKQSMCE1vqqmD+2pv6+IdEjmopFatacvg==
unrs-resolver@^1.9.2:
unrs-resolver@^1.7.11, unrs-resolver@^1.9.2:
version "1.11.1"
resolved "https://registry.yarnpkg.com/unrs-resolver/-/unrs-resolver-1.11.1.tgz#be9cd8686c99ef53ecb96df2a473c64d304048a9"
integrity sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==