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

Disallow truthiness/nullishness checks on syntax that never varies on it #59217

Merged
merged 13 commits into from
Jul 22, 2024

Conversation

RyanCavanaugh
Copy link
Member

@RyanCavanaugh RyanCavanaugh commented Jul 10, 2024

Inspired by @mjbvz's suggestion to disallow if (/regex/), this PR implements a series of checks that mostly overlap with ESLint's no-constant-binary-expression rule, which found some great stuff in their v8.14.

The rule here is that it's an error to check for the truthiness of an expression which has "static truthy semantics", that is, always evaluates to the same truthiness. Examples:

  • if ([someArr]) { is illegal (array literals are always truthy)
  • const foo = { ...someObj } || { } is illegal (object literals are always truthy, even those only spread in null)

The expressions false, true, 0, and 1 are excluded from this check, since code like while (true) {, while(1) {, do { } while (false), if (true || i_am_just_debugging) { are common and unlikely to represent programmer errors.

The same rule is applied to nullishness (the thing ?? checks for). Because ??'s precedence can be surprising, this finds a huge number of "missing parens" bugs.

This check is entirely syntactic, because with noUncheckedIndexAccess off, "hidden" nulls/undefined are idiomatically observed in a variety of places:

function idx(arr: string[], index: number) {
    return arr[index] ?? "out of bounds";
}

A breakdown of all the user test hits can be found at #59217 (comment) (part 1) and #59217 (comment) (part 2).

There are a lot, but 100% of these appear to be legitimate bugs in code, so I don't think it's necessary to add a flag for this.

@typescript-bot typescript-bot added Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug labels Jul 10, 2024
@RyanCavanaugh
Copy link
Member Author

@typescript-bot test it
@typescript-bot test top800

@typescript-bot
Copy link
Collaborator

typescript-bot commented Jul 10, 2024

Starting jobs; this comment will be updated as builds start and complete.

Command Status Results
test top400 ✅ Started 👀 Results
user test this ✅ Started 👀 Results
run dt ✅ Started 👀 Results
perf test this faster ✅ Started 👀 Results
test top800 ✅ Started 👀 Results

@typescript-bot
Copy link
Collaborator

Hey @RyanCavanaugh, the results of running the DT tests are ready.

There were interesting changes:

Branch only errors:

Package: ember__component/v3
Error:

Error: 
/mnt/vss/_work/1/DefinitelyTyped/types/ember__component/v3/test/component.ts
  104:13  error  TypeScript@local compile error: 
This expression is always truthy. Did you mean to test something else?  @definitelytyped/expect

✖ 1 problem (1 error, 0 warnings)

    at combineErrorsAndWarnings (/mnt/vss/_work/1/DefinitelyTyped/node_modules/.pnpm/@definitelytyped+dtslint@0.2.22_typescript@5.6.0-dev.20240709/node_modules/@definitelytyped/dtslint/dist/index.js:194:28)
    at runTests (/mnt/vss/_work/1/DefinitelyTyped/node_modules/.pnpm/@definitelytyped+dtslint@0.2.22_typescript@5.6.0-dev.20240709/node_modules/@definitelytyped/dtslint/dist/index.js:186:20)

Package: hapi__joi
Error:

Error: 
/mnt/vss/_work/1/DefinitelyTyped/types/hapi__joi/hapi__joi-tests.ts
  52:25  error  TypeScript@local compile error: 
This expression is always truthy. Did you mean to test something else?  @definitelytyped/expect

✖ 1 problem (1 error, 0 warnings)

    at combineErrorsAndWarnings (/mnt/vss/_work/1/DefinitelyTyped/node_modules/.pnpm/@definitelytyped+dtslint@0.2.22_typescript@5.6.0-dev.20240709/node_modules/@definitelytyped/dtslint/dist/index.js:194:28)
    at runTests (/mnt/vss/_work/1/DefinitelyTyped/node_modules/.pnpm/@definitelytyped+dtslint@0.2.22_typescript@5.6.0-dev.20240709/node_modules/@definitelytyped/dtslint/dist/index.js:186:20)

Package: karma-browserstack-launcher
Error:

Error: 
/mnt/vss/_work/1/DefinitelyTyped/types/karma-browserstack-launcher/karma-browserstack-launcher-tests.ts
   9:23  error  TypeScript@local compile error: 
This expression is always truthy. Did you mean to test something else?  @definitelytyped/expect
  10:24  error  TypeScript@local compile error: 
This expression is always truthy. Did you mean to test something else?  @definitelytyped/expect

✖ 2 problems (2 errors, 0 warnings)

    at combineErrorsAndWarnings (/mnt/vss/_work/1/DefinitelyTyped/node_modules/.pnpm/@definitelytyped+dtslint@0.2.22_typescript@5.6.0-dev.20240709/node_modules/@definitelytyped/dtslint/dist/index.js:194:28)
    at runTests (/mnt/vss/_work/1/DefinitelyTyped/node_modules/.pnpm/@definitelytyped+dtslint@0.2.22_typescript@5.6.0-dev.20240709/node_modules/@definitelytyped/dtslint/dist/index.js:186:20)

Package: ember__component
Error:

Error: 
/mnt/vss/_work/1/DefinitelyTyped/types/ember__component/test/component.ts
  104:13  error  TypeScript@local compile error: 
This expression is always truthy. Did you mean to test something else?  @definitelytyped/expect

✖ 1 problem (1 error, 0 warnings)

    at combineErrorsAndWarnings (/mnt/vss/_work/1/DefinitelyTyped/node_modules/.pnpm/@definitelytyped+dtslint@0.2.22_typescript@5.6.0-dev.20240709/node_modules/@definitelytyped/dtslint/dist/index.js:194:28)
    at runTests (/mnt/vss/_work/1/DefinitelyTyped/node_modules/.pnpm/@definitelytyped+dtslint@0.2.22_typescript@5.6.0-dev.20240709/node_modules/@definitelytyped/dtslint/dist/index.js:186:20)

Package: rsvp
Error:

Error: 
/mnt/vss/_work/1/DefinitelyTyped/types/rsvp/rsvp-tests.ts
  104:13  error  TypeScript@local compile error: 
This expression is always truthy. Did you mean to test something else?  @definitelytyped/expect

✖ 1 problem (1 error, 0 warnings)

    at combineErrorsAndWarnings (/mnt/vss/_work/1/DefinitelyTyped/node_modules/.pnpm/@definitelytyped+dtslint@0.2.22_typescript@5.6.0-dev.20240709/node_modules/@definitelytyped/dtslint/dist/index.js:194:28)
    at runTests (/mnt/vss/_work/1/DefinitelyTyped/node_modules/.pnpm/@definitelytyped+dtslint@0.2.22_typescript@5.6.0-dev.20240709/node_modules/@definitelytyped/dtslint/dist/index.js:186:20)

Package: ember/v2
Error:

Error: 
/mnt/vss/_work/1/DefinitelyTyped/types/ember/v2/test/component.ts
  105:13  error  TypeScript@local compile error: 
This expression is always truthy. Did you mean to test something else?  @definitelytyped/expect

✖ 1 problem (1 error, 0 warnings)

    at combineErrorsAndWarnings (/mnt/vss/_work/1/DefinitelyTyped/node_modules/.pnpm/@definitelytyped+dtslint@0.2.22_typescript@5.6.0-dev.20240709/node_modules/@definitelytyped/dtslint/dist/index.js:194:28)
    at runTests (/mnt/vss/_work/1/DefinitelyTyped/node_modules/.pnpm/@definitelytyped+dtslint@0.2.22_typescript@5.6.0-dev.20240709/node_modules/@definitelytyped/dtslint/dist/index.js:186:20)

Package: react-native-joi
Error:

Error: 
/mnt/vss/_work/1/DefinitelyTyped/types/react-native-joi/react-native-joi-tests.ts
  53:25  error  TypeScript@local compile error: 
This expression is always truthy. Did you mean to test something else?  @definitelytyped/expect

✖ 1 problem (1 error, 0 warnings)

    at combineErrorsAndWarnings (/mnt/vss/_work/1/DefinitelyTyped/node_modules/.pnpm/@definitelytyped+dtslint@0.2.22_typescript@5.6.0-dev.20240709/node_modules/@definitelytyped/dtslint/dist/index.js:194:28)
    at runTests (/mnt/vss/_work/1/DefinitelyTyped/node_modules/.pnpm/@definitelytyped+dtslint@0.2.22_typescript@5.6.0-dev.20240709/node_modules/@definitelytyped/dtslint/dist/index.js:186:20)

Package: ember/v3
Error:

Error: 
/mnt/vss/_work/1/DefinitelyTyped/types/ember/v3/test/component.ts
  105:13  error  TypeScript@local compile error: 
This expression is always truthy. Did you mean to test something else?  @definitelytyped/expect

✖ 1 problem (1 error, 0 warnings)

    at combineErrorsAndWarnings (/mnt/vss/_work/1/DefinitelyTyped/node_modules/.pnpm/@definitelytyped+dtslint@0.2.22_typescript@5.6.0-dev.20240709/node_modules/@definitelytyped/dtslint/dist/index.js:194:28)
    at runTests (/mnt/vss/_work/1/DefinitelyTyped/node_modules/.pnpm/@definitelytyped+dtslint@0.2.22_typescript@5.6.0-dev.20240709/node_modules/@definitelytyped/dtslint/dist/index.js:186:20)

Package: ember
Error:

Error: 
/mnt/vss/_work/1/DefinitelyTyped/types/ember/test/component.ts
  105:13  error  TypeScript@local compile error: 
This expression is always truthy. Did you mean to test something else?  @definitelytyped/expect

✖ 1 problem (1 error, 0 warnings)

    at combineErrorsAndWarnings (/mnt/vss/_work/1/DefinitelyTyped/node_modules/.pnpm/@definitelytyped+dtslint@0.2.22_typescript@5.6.0-dev.20240709/node_modules/@definitelytyped/dtslint/dist/index.js:194:28)
    at runTests (/mnt/vss/_work/1/DefinitelyTyped/node_modules/.pnpm/@definitelytyped+dtslint@0.2.22_typescript@5.6.0-dev.20240709/node_modules/@definitelytyped/dtslint/dist/index.js:186:20)

You can check the log here.

@jakebailey
Copy link
Member

Wow, not entirely related but the new "build mode continues on error" makes the self test super noisy, it's like some files aren't getting emitted from our repo or something and that really breaks analysis. I assume because we use noEmitOnError (probably we shouldn't?)

@jakebailey
Copy link
Member

I think the ?? checks are dubious; from the self check, this code is marked as bad:

const end = (endMarkName !== undefined ? marks.get(endMarkName) : undefined) ?? timestamp();

I don't see a bug in this case; is this just a bug in the rule?

@typescript-bot
Copy link
Collaborator

Looks like you're introducing a change to the public API surface area. If this includes breaking changes, please document them on our wiki's API Breaking Changes page.

Also, please make sure @DanielRosenwasser and @RyanCavanaugh are aware of the changes, just as a heads up.

@microsoft microsoft deleted a comment from typescript-bot Jul 10, 2024
@microsoft microsoft deleted a comment from typescript-bot Jul 10, 2024
@microsoft microsoft deleted a comment from typescript-bot Jul 10, 2024
@microsoft microsoft deleted a comment from typescript-bot Jul 10, 2024
@microsoft microsoft deleted a comment from typescript-bot Jul 10, 2024
@microsoft microsoft deleted a comment from typescript-bot Jul 10, 2024
@microsoft microsoft deleted a comment from typescript-bot Jul 10, 2024
@microsoft microsoft deleted a comment from typescript-bot Jul 10, 2024
@microsoft microsoft deleted a comment from typescript-bot Jul 10, 2024
@RyanCavanaugh
Copy link
Member Author

Yeah, I forgot ternaries, plus some other bugs. A lot of good finds in there though.

@typescript-bot test top800

@typescript-bot
Copy link
Collaborator

typescript-bot commented Jul 10, 2024

Starting jobs; this comment will be updated as builds start and complete.

Command Status Results
test top800 ✅ Started 👀 Results

@@ -920,6 +920,14 @@ export const enum RelationComparisonResult {
ReportsMask = ReportsUnmeasurable | ReportsUnreliable,
}

/** @internal */
export const enum PredicateSemantics {
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 Ternary would actually cover this, if you didn't want to come up with something new.

@@ -8705,6 +8705,7 @@ declare namespace ts {
* ```
*/
function getJSDocCommentsAndTags(hostNode: Node): readonly (JSDoc | JSDocTag)[];
function isBinaryLogicalOperator(token: SyntaxKind): boolean;
Copy link
Member

Choose a reason for hiding this comment

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

Do we want /** @internal */ on this? (I know it's a draft but before I forget)

@typescript-bot
Copy link
Collaborator

@RyanCavanaugh Here are the results of running the top 800 repos with tsc comparing main and refs/pull/59217/merge:

Something interesting changed - please have a look.

Details

AbdullahAlfaraj/Auto-Photoshop-StableDiffusion-Plugin

typescripts/tsconfig.json

ariakit/ariakit

1 of 6 projects failed to build with the old tsc and were ignored

tsconfig.json

website/tsconfig.json

packages/ariakit-react-core/tsconfig.build.json

bitwarden/clients

12 of 56 projects failed to build with the old tsc and were ignored

libs/common/tsconfig.spec.json

libs/common/tsconfig.json

libs/angular/tsconfig.spec.json

libs/angular/tsconfig.json

bitwarden_license/bit-cli/tsconfig.spec.json

bitwarden_license/bit-cli/tsconfig.json

apps/cli/tsconfig.spec.json

apps/cli/tsconfig.json

ChatGPTNextWeb/ChatGPT-Next-Web

tsconfig.json

  • error TS2871: Using ?? on this expression appears unintentional because it always evaluates to the same nullishness.
  • error TS2869: This expression is always truthy. Did you mean to test something else?

clash-verge-rev/clash-verge-rev

tsconfig.json

dream-num/univer

6 of 48 projects failed to build with the old tsc and were ignored

packages/uniscript/tsconfig.json

packages/ui/tsconfig.json

packages/thread-comment-ui/tsconfig.json

packages/slides-ui/tsconfig.json

packages/sheets-zen-editor/tsconfig.json

packages/sheets-ui/tsconfig.json

packages/sheets-thread-comment/tsconfig.json

packages/sheets-sort-ui/tsconfig.json

packages/sheets-numfmt/tsconfig.json

packages/sheets-hyper-link-ui/tsconfig.json

packages/sheets-formula/tsconfig.json

packages/sheets-find-replace/tsconfig.json

packages/sheets-filter-ui/tsconfig.json

packages/sheets-drawing-ui/tsconfig.json

packages/sheets-data-validation/tsconfig.json

packages/find-replace/tsconfig.json

packages/drawing-ui/tsconfig.json

packages/docs-ui/tsconfig.json

packages/docs-thread-comment-ui/tsconfig.json

packages/docs-hyper-link-ui/tsconfig.json

packages/docs-drawing-ui/tsconfig.json

packages/docs-drawing/tsconfig.json

packages/debugger/tsconfig.json

ethers-io/ethers.js

2 of 6 projects failed to build with the old tsc and were ignored

tsconfig.types.json

facebook/lexical

6 of 12 projects failed to build with the old tsc and were ignored

tsconfig.json

tsconfig.build.json

gpbl/react-day-picker

tsconfig.json

tsconfig-esm.json

tsconfig-docs.json

tsconfig-cjs.json

graphile/crystal

5 of 11 projects failed to build with the old tsc and were ignored

tsconfig.json

labring/FastGPT

1 of 8 projects failed to build with the old tsc and were ignored

projects/app/tsconfig.json

Licoy/ChatGPT-Midjourney

tsconfig.json

  • error TS2871: Using ?? on this expression appears unintentional because it always evaluates to the same nullishness.
  • error TS2869: This expression is always truthy. Did you mean to test something else?

material-components/material-web

2 of 3 projects failed to build with the old tsc and were ignored

scripts/tsconfig.json

material-shell/material-shell

tsconfig.types.json

  • error TS2871: Using ?? on this expression appears unintentional because it always evaluates to the same nullishness.

tsconfig.json

  • error TS2871: Using ?? on this expression appears unintentional because it always evaluates to the same nullishness.

microsoft/azuredatastudio

8 of 67 projects failed to build with the old tsc and were ignored

extensions/github-authentication/tsconfig.json

build/tsconfig.build.json

  • error TS2871: Using ?? on this expression appears unintentional because it always evaluates to the same nullishness.

microsoft/vscode

4 of 54 projects failed to build with the old tsc and were ignored

src/tsconfig.tsec.json

src/tsconfig.json

extensions/github-authentication/tsconfig.json

build/tsconfig.build.json

  • error TS2871: Using ?? on this expression appears unintentional because it always evaluates to the same nullishness.

motion-canvas/motion-canvas

13 of 18 projects failed to build with the old tsc and were ignored

packages/vite-plugin/tsconfig.json

naver/billboard.js

tsconfig.json

openblocks-dev/openblocks

5 of 8 projects failed to build with the old tsc and were ignored

client/packages/openblocks-design/tsconfig.json

portainer/portainer

tsconfig.json

react-page/react-page

11 of 13 projects failed to build with the old tsc and were ignored

packages/editor/tsconfig-es.json

reactjs/react.dev

tsconfig.json

rolling-scopes/rsschool-app

2 of 5 projects failed to build with the old tsc and were ignored

nestjs/tsconfig.json

nestjs/tsconfig.build.json

Shopify/react-native-skia

3 of 10 projects failed to build with the old tsc and were ignored

fabricexample/tsconfig.json

example/tsconfig.json

supabase/supabase

31 of 37 projects failed to build with the old tsc and were ignored

apps/studio/tsconfig.json

apps/docs/tsconfig.json

tailwindlabs/headlessui

1 of 5 projects failed to build with the old tsc and were ignored

packages/@headlessui-react/tsconfig.json

tremorlabs/tremor

tsconfig.json

Uniswap/web3-react

4 of 14 projects failed to build with the old tsc and were ignored

packages/core/tsconfig.json

Vendicated/Vencord

1 of 2 projects failed to build with the old tsc and were ignored

tsconfig.json

vercel/ai-chatbot

tsconfig.json

withfig/autocomplete

tsconfig.json

zu1k/bs-core

frontend/tsconfig.json

@RyanCavanaugh
Copy link
Member Author

Not done looking at all of these yet, but so far a 100% correctness rate...

AbdullahAlfaraj/Auto-Photoshop-StableDiffusion-Plugin

Definitely a bug:

<div key={`${node_id}_${name}__${index}` ?? void 0}>

ariakit/ariakit

Definitely a bug:

horizontal = itemObject.orientation === "horizontal" ?? horizontal;

bitwarden/clients

Definitely a bug:

map((options) => options?.trustedDeviceOption != null ?? false),

ChatGPTNextWeb/ChatGPT-Next-Web

Definitely a bug:

if (historyMsgLength > modelConfig?.max_tokens ?? 4000) {

clash-verge-rev/clash-verge-rev

Definitely a bug:

setTheme({ ...theme_setting } || {});

dream-num/univer

Definitely bugs (4!):

// Two instances of this:
undos: [] || undoMutations,

let fontFamily = localeService.t(`fontFamily.${(`${value}` ?? '').replace(/\s/g, '')}`);

        <span className={styles.uiPluginSheetsFontFamilyItem} style={{ fontFamily: value }}>
            {localeService.t(`fontFamily.${(`${value}` ?? '').replace(/\s/g, '')}`)}
        </span>

ethers-io/ethers.js

Definitely a subtle parenthesization bug:

                    if (isError(error, "CANCELLED") || isError(error, "BAD_DATA") ||
                        isError(error, "NETWORK_ERROR" || isError(error, "UNSUPPORTED_OPERATION"))) {

facebook/lexical

Definitely a bug:

   {...prevStyles} || {},

gpbl/react-day-picker

Definitely a bug:

if (monthsDiff < numberOfMonths ?? 1) {

graphile/crystal

Definitely a bug:

      const nonNullableAttribute = this.codec.attributes
        ? Object.entries(this.codec.attributes).find(
            ([_attributeName, spec]) =>
              !spec.via && !spec.expression && spec.notNull,
          )?.[0]
        : null ?? pk?.attributes[0];

labring/FastGPT

Definitely three bugs:

<QuestionTip ml={1} label={publishT('QPM Tips' || '')}></QuestionTip>
// Second instance of above
<QuestionTip ml={1} label={publishT('QPM Tips' || '')}></QuestionTip>

            <QuestionTip
              ml={1}
              label={t('support.outlink.share.Response Quote tips' || '')}
            ></QuestionTip>

Licoy/ChatGPT-Midjourney

Definitely a bug:

if (historyMsgLength > modelConfig?.max_tokens ?? 4000) {

material-components/material-web

Definitely a bug:

    methodsTable.addRow([
      `\`${rowObj.name}\``,
      rowObj.parameters.map((p) => `\`${p.name}\``).join(', ') || '_None_',
      `\`${rowObj.returns}\`` ?? '`void`',
      rowObj.description ?? '',
    ]);

material-shell/material-shell

Definitely a bug:

    const msWorkspaceIsInFloatLayout =
        metaWindow.msWindow?.msWorkspace.layout.state.key === 'float' ?? false;

microsoft/azuredatastudio

Definitely two bugs:

this._logger.warn('Failed to delete token from server.' + e.message ?? e);
// and
const verbose = !!options.verbose ?? (!!process.env['CI'] || !!process.env['BUILD_ARTIFACTSTAGINGDIRECTORY']);

microsoft/vscode

Many bugs!

uselessReplaceKey5: '@ham' || '',

// 3 instances of this
assert.ok(!'Unexpected value ' + basename(value.resource.fsPath));

const contentHeight = (editor._getViewModel()?.getLineCount()! * lineHeight) ?? editor.getContentHeight();

singlePerResource: () => !this.getCustomEditorCapabilities(contributedEditor.id)?.supportsMultipleEditorsPerDocument ?? true

tooltip: (`${exitMessage} ` ?? '') + nls.localize('launchFailed.exitCodeOnlyShellIntegration', 'Disabling shell integration in user settings might help.')

return !!this._chatWidget?.rawValue?.hasFocus() ?? false

return this.viewModel?.welcomeExperience === WelcomeExperience.ForWorkspace ?? true;

return !item?.handle.startsWith('vscode-command:') ?? false;

this._logger.warn('Failed to delete token from server.' + e.message ?? e);

const verbose = !!options.verbose ?? (!!process.env['CI'] || !!process.env['BUILD_ARTIFACTSTAGINGDIRECTORY']);

motion-canvas/motion-canvas

Definitely a bug:

throw 'Unexpected Status: ' + result.statusCode ?? 'NO_STATUS';

naver/billboard.js

Definitely a bug, but super unclear what they meant here:

if (isArcishData && "id") {

twofer:

function getScrollPosition(node: HTMLElement) {
	return {
		x: (window.pageXOffset ?? window.scrollX ?? 0) + node.scrollLeft ?? 0,
		y: (window.pageYOffset ?? window.scrollY ?? 0) + node.scrollTop ?? 0
	};
}

openblocks-dev/openblocks

  width: ${(props) =>
    props.placement === "right"
      ? "calc(100% - 96px)"
      : "bottom"
      ? "calc(100% - 112px)"
      : "calc(100% - 136px"};
  flex-grow: 1;

and

const LabelWrapper = styled.div<{ placement: ControlPlacement }>`
  flex-shrink: 0;
  width: ${(props) => (props.placement === "right" ? "96px" : "bottom" ? "112px" : "136px")};
`;

@RyanCavanaugh
Copy link
Member Author

(wiping sweat from brow) They're literally all bugs

portainer/portainer

Definitely a bug:

Name: `${node.metadata?.name}${isApi ? 'api' : ''}` ?? ''

react-page/react-page

Definitely a bug:

  return useNodeProps(nodeId, (node) =>
    isRow(node)
      ? node.cells?.length > 0 ?? false
      : (node?.rows?.length ?? 0) > 0 ?? false
  );

reactjs/react.dev

Definitely a bug (what exactly were they trying to do here?):

    throw new Error(
      'Expected name, title, permalink, and children for ' + name ??
        title ??
        permalink ??
        'unknown'
    );

rollingscopes/rsschool-app

Definitely a bug:

 await this.courseRepository.query(
      `select "name", "id", 'event' as "type"  from "event" where "id" = ANY($1)
        union
       select "name", "id", 'task' as "type"  from "task" where "id" = ANY($2)`,
      [fetchEvents.size > 0 ? [...fetchEvents] : null, [...fetchTasks] || null],
    );

Shopify/react-native-skia

Definitely a bug:

    ps.textAlign =
      value.textAlign !== undefined
        ? { value: value.textAlign }
        : undefined ?? ps.textAlign;

supabase/supabase

Definitely 6 bugs here:

const isNotOrgWithPartnerBilling = !subscription?.billing_via_partner ?? true;
// and
const isAcknowledged = typeof window !== 'undefined' ? localStorage?.getItem(key) === 'true' ?? false : false
// and
function checkFeature(feature: Feature, features?: Feature[]) {
  return !features?.includes(feature) ?? true
}
// and 2 instances of this:
layout: 'vertical' || 'flex',
// and
    <img
       src={`${BASE_PATH}` + menu.icon ?? `/img/icons/menu/${id}.svg`}
       className="w-5 rounded"
    />

tailwindlabs/headlessui

Definitely a bug:

let isParentDisabled = parent?.getAttribute('disabled') === '' ?? false

tremorlabs/tremor

Two bugs with eight manifestations here:

disabled={!hasScroll?.left ?? true}
disabled={!hasScroll?.right ?? true}
// six instances of this exact line:
const prev = previousValues ? previousValues[dataKey] : undefined;
const percentage = ((value - prev) / prev) * 100 ?? undefined;

Uniswap/web3-react

This appears to be less of a mechanical error and more of just a wat. This isn't how React actually works; unclear if there's relevant syntactic processing going on here or what.

https://github.com/Uniswap/web3-react/blob/bd267025f06b5808fd6ccc4e18dde4d400c4076a/packages/core/src/hooks.ts#L360

// to ensure connectors remain fresh, we condition re-renders on loaded, isActive and chainId
void loaded && isActive && chainId

Vendicated/Vencord

Definitely a bug:

    wrapSort(comparator: Function, row: any) {
        return row.type === 5
            ? -UserAffinitiesStore.getUserAffinity(row.user.id)?.affinity ?? 0
            : comparator(row);
    },

vercel/ai-chatbot

Definitely a bug, though I don't know what they really meant here

const fileName = window.prompt('Enter file name' || '', suggestedFileName)

withfig/autocomplete

Definitely a bug:

const name = ("FileSystemArn" ? elm["FileSystemArn"] : elm) as string;

zu1k/bs-core

Definitely a bug:

                  <Description name={`${t('book.md5') ?? 'MD5'}: `}>
                    {(
                      <Button
                        colorScheme="gray"
                        variant="ghost"
                        size="xs"
                        leftIcon={<LinkIcon />}
                        onClick={() => {
                          navigator.clipboard.writeText(md5?.toLowerCase() ?? 'Unknown');
                          setMd5Copied(true);
                          setTimeout(() => {
                            setMd5Copied(false);
                          }, 2000);
                        }}
                      >
                        {md5Copied ? t('copied') : md5}
                      </Button>
                    ) ||
                      t('book.unknown') ||
                      'Unknown'}
                  </Description>

@RyanCavanaugh
Copy link
Member Author

RyanCavanaugh commented Jul 11, 2024

Coallating the errors that people seem to be making here. 100.0% of this code is extremely indicative of a logic error of some sort.

  • "Tunnel vision" where the left operand of ?? being !ed isn't apparent, or they forgot parens
    • Example: const p = !someSettingName ?? theDefault
    • supabase/supabase
    • tremorlabs/tremor
    • Vendicated/Vencord
  • Forgot to fully process the left operand
    • Example: const name = ("FileSystemArn" ? elm["FileSystemArn"] : elm)
    • withfig/autocomplete
  • Subtle parens misplacement
    • Example: if (isError(a, "foo") || isError(c, "bar" || isError(d, "baz")) {
    • labring/FastGPT
    • ethers-io/ethers.js
    • zu1k/bs-core ||'d a JSX element
  • Parsing precedence confusion on how a + b ?? c parses
    • It's (a + b) ?? c, not a + (b ?? c)
    • microsoft/azuredatastudio
    • motion-canvas/motion-canvas
    • naver/billboard.js
    • supabase/supabase
  • Parsing precedence confusion, using ?? on a binary op whose right operand is possibly-nullish
    • Difficult to hit without any since number > undefined is not legal
    • Example: if (historyMsgLength > modelConfig?.max_tokens ?? 4000)
    • ChatGPTNextWeb/ChatGPT-Next-Web
    • gpbl/react-day-picker
    • Licoy/ChatGPT-Midjourney
  • Parsing precedense confusion on ? :
    • Example: const val = cond ? arr[idx] : undefined ?? "fallback"
      • This is a coalesce on undefined ?? "fallback", not a coalesce on the ternary
    • These are so subtle it's likely people will think TypeScript is wrong, but it's not
    • graphile/crystal
    • Shopify/react-native-skia
  • Code that is trying to handle impossible conditions, likely due to underconfidence in language semantics?
    • Example: const msg = `foo_${bar}` ?? "empty"
    • Example: const isReady = options?.ready != null ?? false
    • Example: const isNeat = a > b ?? false
    • People seem to think comparisons involving NaN might be nullish? Hard to tell
    • AbdullahAlfaraj/Auto-Photoshop-StableDiffusion-Plugin
    • material-shell/material-shell
    • portainer/portainer
    • react-page/react-page
    • supabase/supabase
    • tailwindlabs/headlessui
    • tremorlabs/tremor
  • Possible left/right typos?
    • Example: processArray([] || someArray);
    • dream-num/univer
  • Likely misapprehension that { ...null } is null, not { }, same for `${value}`, same for [...someArray]
    • Example: const opts = { ...someOpts } || { }
    • clash-verge-rev/clash-verge-rev
    • dream-num/univer
    • facebook/lexical
    • material-components/material-web
    • rollingscopes/rsschool-app
  • Critical ternary overflow confused a programmer
    • Example: const test = a ? b : c ? d : "e" ? f : g;
    • openblocks-dev/openblocks x2
    • Somehow none in our codebase.
  • No idea how this went wrong or what they intended to do
    • naver/billboard.js: if (isArcishData && "id") {
    • reactjs/react.dev: throw new Error("Expected a, b, c, d : + a ?? b ?? c ?? d ?? "unknown")
    • supabase/supabase has "vertical" || "flex" and didn't seem to understand what I was pointing out
    • vercel/ai-chatbot has window.prompt("Enter file name" || "")
    • Uniswap/web3-react maybe uses a syntactic postprocessor? void foo && bar && baz, ?

@RyanCavanaugh
Copy link
Member Author

@typescript-bot pack this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Jul 15, 2024

Starting jobs; this comment will be updated as builds start and complete.

Command Status Results
pack this ✅ Started ✅ Results

@typescript-bot
Copy link
Collaborator

typescript-bot commented Jul 15, 2024

Hey @RyanCavanaugh, I've packed this into an installable tgz. You can install it for testing by referencing it in your package.json like so:

{
    "devDependencies": {
        "typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/162747/artifacts?artifactName=tgz&fileId=D85B944B6DC9FC160915DE81F57675BC735E4F2ADAA4D4524371030F9A5BB8FC02&fileName=/typescript-5.6.0-insiders.20240715.tgz"
    }
}

and then running npm install.


There is also a playground for this build and an npm module you can use via "typescript": "npm:@typescript-deploys/pr-build@5.6.0-pr-59217-14".;

@RyanCavanaugh RyanCavanaugh force-pushed the noSuspectTruthyChecks branch from 062703b to 701b54a Compare July 19, 2024 23:08
Copy link
Member

@jakebailey jakebailey left a comment

Choose a reason for hiding this comment

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

LGTM but needs baseline updates and fmt.

@sandersn
Copy link
Member

I've barely skimmed the code, but I looked at the error messages and tests and both are in a good place.

src/compiler/checker.ts Outdated Show resolved Hide resolved
@@ -17,6 +17,7 @@ enum E { x }

var a: any;
>a : any
> : ^^^
Copy link
Member

Choose a reason for hiding this comment

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

What happened that updated the types baselines like this?

Copy link
Member

Choose a reason for hiding this comment

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

This is a goofy quirk of the types baselines; in typeWriter.ts:

// Distinguish `errorType`s from `any`s; but only if the file has no errors.

So this types file changed because computedPropertyNames48_ES5.errors.txt was emitted.

Copy link
Member

Choose a reason for hiding this comment

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

(Honestly, I wonder if it's worth the special case; I noticed it separately while working on something else)

Copy link
Member

@DanielRosenwasser DanielRosenwasser left a comment

Choose a reason for hiding this comment

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

Minor nits, but I would actually be okay with merging as-is.

src/compiler/checker.ts Outdated Show resolved Hide resolved
src/compiler/checker.ts Outdated Show resolved Hide resolved
@DanielRosenwasser
Copy link
Member

Actually - if you could make sure all of the outer expression cases at least have tests that'd be ideal.

@DanielRosenwasser
Copy link
Member

@typescript-bot pack this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Jul 22, 2024

Starting jobs; this comment will be updated as builds start and complete.

Command Status Results
pack this ✅ Started ✅ Results

@typescript-bot
Copy link
Collaborator

typescript-bot commented Jul 22, 2024

Hey @DanielRosenwasser, I've packed this into an installable tgz. You can install it for testing by referencing it in your package.json like so:

{
    "devDependencies": {
        "typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/162926/artifacts?artifactName=tgz&fileId=E3ECFACBC9BCFD1AF7B152E4ABD9514823CAD7566ADB181EC4E51323D6AC433E02&fileName=/typescript-5.6.0-insiders.20240722.tgz"
    }
}

and then running npm install.


There is also a playground for this build and an npm module you can use via "typescript": "npm:@typescript-deploys/pr-build@5.6.0-pr-59217-23".;

@spenceryue
Copy link

spenceryue commented Nov 27, 2024

Could there be a tsconfig option to disable this feature?

Below are some patterns that no longer work after this change:

"hardcoded value" ?? /* not working yet */ realValue()

"TODO fix this" && { not: "fully working yet" }

"force 'A' until TICKET-123 resolves things" || condition() ? "A" : "B"

{ width: 123 ?? /* Doesn't work */ `calc(100vh - var(--TOP_NAV_HEIGHT, 0px) + ${TOKENS.SPACING[4]}px)` }

Comments could be used, but then disabled code is harder to separate from commented text:

"hardcoded value" /* not working yet `realValue()` */

/* TODO fix this */ { not: "fully working yet" }

/* force 'A' until TICKET-123 resolves things */ true || condition() ? "A" : "B"

{ width: 123 /* Doesn't work `` `calc(100vh - var(--TOP_NAV_HEIGHT, 0px) + ${TOKENS.SPACING[4]}px)` `` */ }

Other ergonomic considerations:

  • Prettier moves comments around making it difficult to place next to the target code
  • Toggling between branches requires more steps
    • Before: Delete 123 ??. (Only `calc(100vh - ...)` remains.)
    • After: Delete 123. Copy/paste `calc(100vh - ...)`.

An advantage of ESLint's no-constant-binary-expression is the ability to configure severity as warn/error. I use warnings 🟡 for "possible logic mistakes", reserving errors 🔴 for "possible runtime errors".

This feature would be more useful to me if it were restricted to if (condition). I'd like to try different configurations to see what works best. If scope would increase too much from more configuration, then a simple toggle like many other existing flags (e.g. allowUnreachableCode, allowUnusedLabels, etc.) would still be very welcome.

Side note: Similarly, I wish TS2742 "This is likely not portable" could be disabled in tsconfig or silenced with @ts-expect-error, because it doesn't correctly factor in package.json#peerDependencies when considering portability.

@RyanCavanaugh
Copy link
Member Author

// @ts-ignore will suppress this. I can't imagine having so many of these patterns that you need to globally re-enable a syntactic trapdoor -- we didn't find any codebases like that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants