Writing TypeScript with AI

| View comments on Hacker News

I’ve had a great experience using Cursor, so I wanted to share how I use it.

Here’s how I tend to use AI in my TypeScript projects. For reference, here are a few projects I’ve worked on with AI recently:

Background

Go over the type of projects I work on. Introduce what a few examples of what I’ve built with AI recently.

Approach

Explain what I do to use AI effectively

Larger Features

Having AIs plan out tasks is essential for larger chunks of work. This has become much easier in Cursor with the introduction of plan mode. Previously I would have the AI write a Markdown document explaining every piece of work it wanted to do. I’d then have it split that document into smaller chunks. Then I’d give it one file at a time until the entire feature was implemented.

I also have the AI identify what commands it might need to run, e.g. to lint, build, or run tests. I put this into the context when building the larger features so it can easily iterate and run the tools that it needs to make progress.

Examples:

Guardrails

Cursor Rules

Cursors rules are very effective at steering the AI towards your style.

More examples:

Static Analysis

I’ve always been a fan of statically typed languages. There are so many kinds of bugs that a compiler can catch. With AI, statically typed languages get another benefit: both you and the AI can have a better understanding of the correctness of generated code.

In practice this means I make sure my tsc and eslint configs are strict. I enable linting with type information so the deeper analysis can be done.

tsconfig.json

{
  "compilerOptions": {
    "strict": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitOverride": true,
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "exactOptionalPropertyTypes": false,
    "noImplicitReturns": true,
    "noPropertyAccessFromIndexSignature": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "useUnknownInCatchVariables": true,
    "noUncheckedSideEffectImports": true,
    "noEmitOnError": true,
    "forceConsistentCasingInFileNames": true
  }
}

eslint.config.ts:

import 
const eslint: {
    readonly meta: {
        readonly name: string;
        readonly version: string;
    };
    readonly configs: {
        readonly recommended: {
            readonly rules: Readonly<Linter.RulesRecord>;
        };
        readonly all: {
            readonly rules: Readonly<Linter.RulesRecord>;
        };
    };
}
eslint
from "@eslint/js";
import { function defineConfig(...args: ConfigWithExtendsArray): Config[]
Helper function to define a config array.
@paramargs The arguments to the function.@returnsThe config array.@throws{TypeError} If no arguments are provided or if an argument is not an object.
defineConfig
} from "eslint/config";
import
const tseslint: {
    config: typeof config;
    configs: {
        all: CompatibleConfigArray;
        base: CompatibleConfig;
        disableTypeChecked: CompatibleConfig;
        eslintRecommended: CompatibleConfig;
        recommended: CompatibleConfigArray;
        recommendedTypeChecked: CompatibleConfigArray;
        recommendedTypeCheckedOnly: CompatibleConfigArray;
        strict: CompatibleConfigArray;
        strictTypeChecked: CompatibleConfigArray;
        strictTypeCheckedOnly: CompatibleConfigArray;
        stylistic: CompatibleConfigArray;
        stylisticTypeChecked: CompatibleConfigArray;
        stylisticTypeCheckedOnly: CompatibleConfigArray;
    };
    parser: CompatibleParser;
    plugin: CompatiblePlugin;
}
tseslint
from "typescript-eslint";
export default function defineConfig(...args: ConfigWithExtendsArray): Config[]
Helper function to define a config array.
@paramargs The arguments to the function.@returnsThe config array.@throws{TypeError} If no arguments are provided or if an argument is not an object.
defineConfig
(
const eslint: {
    readonly meta: {
        readonly name: string;
        readonly version: string;
    };
    readonly configs: {
        readonly recommended: {
            readonly rules: Readonly<Linter.RulesRecord>;
        };
        readonly all: {
            readonly rules: Readonly<Linter.RulesRecord>;
        };
    };
}
eslint
.
configs: {
    readonly recommended: {
        readonly rules: Readonly<Linter.RulesRecord>;
    };
    readonly all: {
        readonly rules: Readonly<Linter.RulesRecord>;
    };
}
configs
.
recommended: {
    readonly rules: Readonly<Linter.RulesRecord>;
}
recommended
,
const tseslint: {
    config: typeof config;
    configs: {
        all: CompatibleConfigArray;
        base: CompatibleConfig;
        disableTypeChecked: CompatibleConfig;
        eslintRecommended: CompatibleConfig;
        recommended: CompatibleConfigArray;
        recommendedTypeChecked: CompatibleConfigArray;
        recommendedTypeCheckedOnly: CompatibleConfigArray;
        strict: CompatibleConfigArray;
        strictTypeChecked: CompatibleConfigArray;
        strictTypeCheckedOnly: CompatibleConfigArray;
        stylistic: CompatibleConfigArray;
        stylisticTypeChecked: CompatibleConfigArray;
        stylisticTypeCheckedOnly: CompatibleConfigArray;
    };
    parser: CompatibleParser;
    plugin: CompatiblePlugin;
}
tseslint
.
configs: {
    all: CompatibleConfigArray;
    base: CompatibleConfig;
    disableTypeChecked: CompatibleConfig;
    eslintRecommended: CompatibleConfig;
    recommended: CompatibleConfigArray;
    recommendedTypeChecked: CompatibleConfigArray;
    recommendedTypeCheckedOnly: CompatibleConfigArray;
    ... 5 more ...;
    stylisticTypeCheckedOnly: CompatibleConfigArray;
}
configs
.strictTypeChecked: CompatibleConfigArray
Contains all of `recommended`, `recommended-type-checked`, and `strict`, along with additional strict rules that require type information.
@see{@link https://typescript-eslint.io/users/configs#strict-type-checked}
strictTypeChecked
,
const tseslint: {
    config: typeof config;
    configs: {
        all: CompatibleConfigArray;
        base: CompatibleConfig;
        disableTypeChecked: CompatibleConfig;
        eslintRecommended: CompatibleConfig;
        recommended: CompatibleConfigArray;
        recommendedTypeChecked: CompatibleConfigArray;
        recommendedTypeCheckedOnly: CompatibleConfigArray;
        strict: CompatibleConfigArray;
        strictTypeChecked: CompatibleConfigArray;
        strictTypeCheckedOnly: CompatibleConfigArray;
        stylistic: CompatibleConfigArray;
        stylisticTypeChecked: CompatibleConfigArray;
        stylisticTypeCheckedOnly: CompatibleConfigArray;
    };
    parser: CompatibleParser;
    plugin: CompatiblePlugin;
}
tseslint
.
configs: {
    all: CompatibleConfigArray;
    base: CompatibleConfig;
    disableTypeChecked: CompatibleConfig;
    eslintRecommended: CompatibleConfig;
    recommended: CompatibleConfigArray;
    recommendedTypeChecked: CompatibleConfigArray;
    recommendedTypeCheckedOnly: CompatibleConfigArray;
    ... 5 more ...;
    stylisticTypeCheckedOnly: CompatibleConfigArray;
}
configs
.stylisticTypeChecked: CompatibleConfigArray
Contains all of `stylistic`, along with additional stylistic rules that require type information.
@see{@link https://typescript-eslint.io/users/configs#stylistic-type-checked}
stylisticTypeChecked
,
{ ConfigObject<RulesConfig>.languageOptions?: LanguageOptions | undefined
An object containing settings related to how the language is configured for linting.
languageOptions
: {
parserOptions: {
    projectService: boolean;
}
parserOptions
: {
projectService: booleanprojectService: true, }, }, }, );
Custom Eslint Rules

In addition to enable the strict default config for eslint, I also write plenty of eslint rules. This is incredibly easy with AI. I can use eslint as an automatic feedback mechanism to force the AI to take a particular approach, avoid certain behaviors, etc.

As an example, I really dislike doing deep nested checks in TypeScript. I much rather use Zod. I have a rule to prevent AI from doing these kinds of checks.

Example:

const const a: unknowna: unknown = {
  
x: {
    y: string;
}
x
: {
y: stringy: "some_value", }, }; // I dislike this if ( typeof const a: unknowna === "object" && const a: object | nulla !== null && "x" in const a: objecta && typeof const a: object & Record<"x", unknown>a.x: unknownx === "object" && const a: object & Record<"x", unknown>a.x: object | nullx !== null && "y" in const a: object & Record<"x", unknown>a.x: objectx && typeof const a: object & Record<"x", unknown>a.x: object & Record<"y", unknown>x.y: unknowny === "string" ) { var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v22.x/lib/console.js)
console
.Console.log(message?: any, ...optionalParams: any[]): void (+3 overloads)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.
@sincev0.1.100
log
(const a: object & Record<"x", unknown>a.x: object & Record<"y", unknown>x.y: stringy); // Finally can access it safely
} // I prefer this import { import zz } from "zod"; const
const schema: z.ZodObject<{
    x: z.ZodObject<{
        y: z.ZodString;
    }, "strip", z.ZodTypeAny, {
        y: string;
    }, {
        y: string;
    }>;
}, "strip", z.ZodTypeAny, {
    x: {
        y: string;
    };
}, {
    x: {
        y: string;
    };
}>
schema
= import zz.
object<{
    x: z.ZodObject<{
        y: z.ZodString;
    }, "strip", z.ZodTypeAny, {
        y: string;
    }, {
        y: string;
    }>;
}>(shape: {
    x: z.ZodObject<{
        y: z.ZodString;
    }, "strip", z.ZodTypeAny, {
        y: string;
    }, {
        y: string;
    }>;
}, params?: z.RawCreateParams): z.ZodObject<{
    x: z.ZodObject<{
        y: z.ZodString;
    }, "strip", z.ZodTypeAny, {
        y: string;
    }, {
        y: string;
    }>;
}, "strip", z.ZodTypeAny, {
    x: {
        y: string;
    };
}, {
    x: {
        y: string;
    };
}>
export object
object
({
x: z.ZodObject<{
    y: z.ZodString;
}, "strip", z.ZodTypeAny, {
    y: string;
}, {
    y: string;
}>
x
: import zz.
object<{
    y: z.ZodString;
}>(shape: {
    y: z.ZodString;
}, params?: z.RawCreateParams): z.ZodObject<{
    y: z.ZodString;
}, "strip", z.ZodTypeAny, {
    y: string;
}, {
    y: string;
}>
export object
object
({
y: z.ZodStringy: import zz.
function string(params?: z.RawCreateParams & {
    coerce?: true;
}): z.ZodString
export string
string
(),
}), }); const
const result: z.SafeParseReturnType<{
    x: {
        y: string;
    };
}, {
    x: {
        y: string;
    };
}>
result
=
const schema: z.ZodObject<{
    x: z.ZodObject<{
        y: z.ZodString;
    }, "strip", z.ZodTypeAny, {
        y: string;
    }, {
        y: string;
    }>;
}, "strip", z.ZodTypeAny, {
    x: {
        y: string;
    };
}, {
    x: {
        y: string;
    };
}>
schema
.
ZodType<{ x: { y: string; }; }, ZodObjectDef<{ x: ZodObject<{ y: ZodString; }, "strip", ZodTypeAny, { y: string; }, { y: string; }>; }, "strip", ZodTypeAny>, { ...; }>.safeParse(data: unknown, params?: z.util.InexactPartial<z.ParseParams>): z.SafeParseReturnType<{
    x: {
        y: string;
    };
}, {
    x: {
        y: string;
    };
}>
safeParse
(const a: unknowna);
if (
const result: z.SafeParseReturnType<{
    x: {
        y: string;
    };
}, {
    x: {
        y: string;
    };
}>
result
.success: booleansuccess) {
var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v22.x/lib/console.js)
console
.Console.log(message?: any, ...optionalParams: any[]): void (+3 overloads)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.
@sincev0.1.100
log
(
const result: z.SafeParseSuccess<{
    x: {
        y: string;
    };
}>
result
.
data: {
    x: {
        y: string;
    };
}
data
.
x: {
    y: string;
}
x
.y: stringy);
} else { // didn't match }

Here’s an example of this rule in my eslint.config.ts:

TODO: we probably need a complete example here

{
  selector: "UnaryExpression[operator='typeof']:not([argument.name='Bun'])",
Left side of comma operator is unused and has no side effects.
message: "Prefer Zod schema validation over typeof operator. Use z.string(), z.number(), etc. instead.",
Left side of comma operator is unused and has no side effects.
';' expected.
Cannot find name 'message'. Did you mean 'onmessage'?
}
Expression expected.

You can also write more complicated eslint rules. For example I noticed the AI occasionally using Zod to typecheck when the types already matched. AI can write these custom rules very effectively, too.

TODO: collapse this

import {
  enum AST_NODE_TYPESAST_NODE_TYPES,
  import ESLintUtilsESLintUtils,
  type import TSESTreeTSESTree,
} from "@typescript-eslint/utils";

const const createRule: <Options extends readonly unknown[], MessageIds extends string>({ meta, name, ...rule }: Readonly<ESLintUtils.RuleWithMetaAndName<Options, MessageIds, unknown>>) => RuleModuleWithName<MessageIds, Options, unknown, ESLintUtils.RuleListener>createRule = import ESLintUtilsESLintUtils.function RuleCreator<unknown>(urlCreator: (ruleName: string) => string): <Options, MessageIds>({ meta, name, ...rule }: Readonly<ESLintUtils.RuleWithMetaAndName<Options, MessageIds, unknown>>) => RuleModuleWithName<MessageIds, Options, unknown, ESLintUtils.RuleListener>
Creates reusable function to create rules with default options and docs URLs.
@paramurlCreator Creates a documentation URL for a given rule name.@returnsFunction to create a rule with the docs URL format.
RuleCreator
(
(name: stringname) => `https://github.com/shepherdjerred/homelab/blob/main/eslint-rules/${name: stringname}.ts`, ); export const const noRedundantZodParse: RuleModuleWithName<"redundantParse", [], unknown, ESLintUtils.RuleListener>noRedundantZodParse = const createRule: <[], "redundantParse">({ meta, name, ...rule }: Readonly<ESLintUtils.RuleWithMetaAndName<[], "redundantParse", unknown>>) => RuleModuleWithName<"redundantParse", [], unknown, ESLintUtils.RuleListener>createRule({ name: stringname: "no-redundant-zod-parse", meta: ESLintUtils.NamedCreateRuleMeta<"redundantParse", unknown, []>meta: { type: "problem" | "layout" | "suggestion"
The type of rule. - `"problem"` means the rule is identifying code that either will cause an error or may cause a confusing behavior. Developers should consider this a high priority to resolve. - `"suggestion"` means the rule is identifying something that could be done in a better way but no errors will occur if the code isn’t changed. - `"layout"` means the rule cares primarily about whitespace, semicolons, commas, and parentheses, all the parts of the program that determine how the code looks rather than how it executes. These rules work on parts of the code that aren’t specified in the AST.
type
: "problem",
docs: RuleMetaDataDocsdocs: { RuleMetaDataDocs.description: string
Concise description of the rule.
description
:
"Disallow parsing values with Zod when the type is already known and matches the schema output", }, messages: Record<"redundantParse", string>
A map of messages which the rule can report. The key is the messageId, and the string is the parameterised error string. See: https://eslint.org/docs/developer-guide/working-with-rules#messageids
messages
: {
redundantParse: stringredundantParse: "Redundant Zod parse: the value '{{valueName}}' already has type '{{valueType}}' which matches the schema output. Zod validation is for unknown/untrusted data.", }, schema: JSONSchema4 | readonly JSONSchema4[]
The options schema. Supply an empty array if there are no options.
schema
: [],
hasSuggestions?: boolean | undefined
Specifies whether rules can return suggestions. Omit if there is no suggestions
hasSuggestions
: false,
}, defaultOptions?: readonly [] | undefined
@deprecatedUse meta.defaultOptions instead
defaultOptions
: [],
create: (context: Readonly<RuleContext<"redundantParse", []>>, optionsWithDefault: readonly []) => ESLintUtils.RuleListenercreate(context: Readonly<RuleContext<"redundantParse", []>>context) { const const services: ParserServicesWithTypeInformationservices = import ESLintUtilsESLintUtils.function getParserServices<"redundantParse", []>(context: Readonly<RuleContext<"redundantParse", []>>): ParserServicesWithTypeInformation (+3 overloads)
Try to retrieve type-aware parser service from context. This **_will_** throw if it is not available.
getParserServices
(context: Readonly<RuleContext<"redundantParse", []>>context);
const const checker: TypeCheckerchecker = const services: ParserServicesWithTypeInformationservices.ParserServicesWithTypeInformation.program: Programprogram.Program.getTypeChecker(): TypeChecker
Gets a type checker that can be used to semantically analyze source files in the program.
getTypeChecker
();
function function (local function) isZodSchema(node: TSESTree.Node): booleanisZodSchema(node: TSESTree.Nodenode: import TSESTreeTSESTree.type Node = TSESTree.CallExpression | TSESTree.AccessorProperty | TSESTree.ArrayExpression | TSESTree.ArrayPattern | TSESTree.ArrowFunctionExpression | TSESTree.AssignmentExpression | TSESTree.AssignmentPattern | TSESTree.AwaitExpression | TSESTree.BinaryExpression | TSESTree.BlockStatement | TSESTree.BreakStatement | TSESTree.CatchClause | TSESTree.ChainExpression | TSESTree.ClassBody | ... 153 more ... | TSESTree.YieldExpressionNode): boolean { try { const const tsNode: TSESTreeToTSNode<TSESTree.Node>tsNode = const services: ParserServicesWithTypeInformationservices.ParserServicesNodeMaps.esTreeNodeToTSNodeMap: ParserWeakMapESTreeToTSNode<TSESTree.Node>esTreeNodeToTSNodeMap.ParserWeakMapESTreeToTSNode<Node>.get<TSESTree.Node>(key: TSESTree.Node): TSESTreeToTSNode<TSESTree.Node>get(node: TSESTree.Nodenode); const const type: Typetype = const checker: TypeCheckerchecker.TypeChecker.getTypeAtLocation(node: Node): TypegetTypeAtLocation(const tsNode: TSESTreeToTSNode<TSESTree.Node>tsNode); const const typeString: stringtypeString = const checker: TypeCheckerchecker.TypeChecker.typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): stringtypeToString(const type: Typetype); return ( const typeString: stringtypeString.String.includes(searchString: string, position?: number): boolean
Returns true if searchString appears as a substring of the result of converting this object to a String, at one or more positions that are greater than or equal to position; otherwise, returns false.
@paramsearchString search string@paramposition If position is undefined, 0 is assumed, so as to search all of the String.
includes
("Zod") ||
const typeString: stringtypeString.String.includes(searchString: string, position?: number): boolean
Returns true if searchString appears as a substring of the result of converting this object to a String, at one or more positions that are greater than or equal to position; otherwise, returns false.
@paramsearchString search string@paramposition If position is undefined, 0 is assumed, so as to search all of the String.
includes
("ZodType") ||
const typeString: stringtypeString.String.includes(searchString: string, position?: number): boolean
Returns true if searchString appears as a substring of the result of converting this object to a String, at one or more positions that are greater than or equal to position; otherwise, returns false.
@paramsearchString search string@paramposition If position is undefined, 0 is assumed, so as to search all of the String.
includes
("ZodString") ||
const typeString: stringtypeString.String.includes(searchString: string, position?: number): boolean
Returns true if searchString appears as a substring of the result of converting this object to a String, at one or more positions that are greater than or equal to position; otherwise, returns false.
@paramsearchString search string@paramposition If position is undefined, 0 is assumed, so as to search all of the String.
includes
("ZodNumber") ||
const typeString: stringtypeString.String.includes(searchString: string, position?: number): boolean
Returns true if searchString appears as a substring of the result of converting this object to a String, at one or more positions that are greater than or equal to position; otherwise, returns false.
@paramsearchString search string@paramposition If position is undefined, 0 is assumed, so as to search all of the String.
includes
("ZodBoolean") ||
const typeString: stringtypeString.String.includes(searchString: string, position?: number): boolean
Returns true if searchString appears as a substring of the result of converting this object to a String, at one or more positions that are greater than or equal to position; otherwise, returns false.
@paramsearchString search string@paramposition If position is undefined, 0 is assumed, so as to search all of the String.
includes
("ZodArray") ||
const typeString: stringtypeString.String.includes(searchString: string, position?: number): boolean
Returns true if searchString appears as a substring of the result of converting this object to a String, at one or more positions that are greater than or equal to position; otherwise, returns false.
@paramsearchString search string@paramposition If position is undefined, 0 is assumed, so as to search all of the String.
includes
("ZodObject") ||
const typeString: stringtypeString.String.includes(searchString: string, position?: number): boolean
Returns true if searchString appears as a substring of the result of converting this object to a String, at one or more positions that are greater than or equal to position; otherwise, returns false.
@paramsearchString search string@paramposition If position is undefined, 0 is assumed, so as to search all of the String.
includes
("ZodRecord") ||
const typeString: stringtypeString.String.includes(searchString: string, position?: number): boolean
Returns true if searchString appears as a substring of the result of converting this object to a String, at one or more positions that are greater than or equal to position; otherwise, returns false.
@paramsearchString search string@paramposition If position is undefined, 0 is assumed, so as to search all of the String.
includes
("ZodUnion") ||
const typeString: stringtypeString.String.includes(searchString: string, position?: number): boolean
Returns true if searchString appears as a substring of the result of converting this object to a String, at one or more positions that are greater than or equal to position; otherwise, returns false.
@paramsearchString search string@paramposition If position is undefined, 0 is assumed, so as to search all of the String.
includes
("ZodLazy") ||
const typeString: stringtypeString.String.includes(searchString: string, position?: number): boolean
Returns true if searchString appears as a substring of the result of converting this object to a String, at one or more positions that are greater than or equal to position; otherwise, returns false.
@paramsearchString search string@paramposition If position is undefined, 0 is assumed, so as to search all of the String.
includes
("ZodLiteral") ||
const typeString: stringtypeString.String.includes(searchString: string, position?: number): boolean
Returns true if searchString appears as a substring of the result of converting this object to a String, at one or more positions that are greater than or equal to position; otherwise, returns false.
@paramsearchString search string@paramposition If position is undefined, 0 is assumed, so as to search all of the String.
includes
("ZodEnum") ||
const typeString: stringtypeString.String.includes(searchString: string, position?: number): boolean
Returns true if searchString appears as a substring of the result of converting this object to a String, at one or more positions that are greater than or equal to position; otherwise, returns false.
@paramsearchString search string@paramposition If position is undefined, 0 is assumed, so as to search all of the String.
includes
("ZodNativeEnum")
); } catch { return false; } } function function (local function) getValueName(node: TSESTree.Node): stringgetValueName(node: TSESTree.Nodenode: import TSESTreeTSESTree.type Node = TSESTree.CallExpression | TSESTree.AccessorProperty | TSESTree.ArrayExpression | TSESTree.ArrayPattern | TSESTree.ArrowFunctionExpression | TSESTree.AssignmentExpression | TSESTree.AssignmentPattern | TSESTree.AwaitExpression | TSESTree.BinaryExpression | TSESTree.BlockStatement | TSESTree.BreakStatement | TSESTree.CatchClause | TSESTree.ChainExpression | TSESTree.ClassBody | ... 153 more ... | TSESTree.YieldExpressionNode): string { if (node: TSESTree.Nodenode.type: AST_NODE_TYPEStype === enum AST_NODE_TYPESAST_NODE_TYPES.function (enum member) AST_NODE_TYPES.Identifier = "Identifier"Identifier) { return node: TSESTree.Identifiernode.Identifier.name: stringname; } return context: Readonly<RuleContext<"redundantParse", []>>context.sourceCode: Readonly<SourceCode>
A SourceCode object that you can use to work with the source that was passed to ESLint.
sourceCode
.function getText(node?: TSESTree.Node | TSESTree.Token, beforeCount?: number, afterCount?: number): string
Gets the source code for the given node.
@paramnode The AST node to get the text for.@parambeforeCount The number of characters before the node to retrieve.@paramafterCount The number of characters after the node to retrieve.@returnsThe text representing the AST node.
getText
(node: TSESTree.CallExpression | TSESTree.AccessorProperty | TSESTree.ArrayExpression | TSESTree.ArrayPattern | TSESTree.ArrowFunctionExpression | TSESTree.AssignmentExpression | TSESTree.AssignmentPattern | TSESTree.AwaitExpression | TSESTree.BinaryExpression | TSESTree.BlockStatement | TSESTree.BreakStatement | TSESTree.CatchClause | TSESTree.ChainExpression | TSESTree.ClassBody | ... 152 more ... | TSESTree.YieldExpressionnode);
} function function (local function) isUnknownOrAny(typeString: string): booleanisUnknownOrAny(typeString: stringtypeString: string): boolean { return typeString: stringtypeString === "unknown" || typeString: stringtypeString === "any"; } function function (local function) isInSafeParseConditional(parseCallNode: TSESTree.CallExpression, schemaNode: TSESTree.Node, argument: TSESTree.Node): booleanisInSafeParseConditional( parseCallNode: TSESTree.CallExpressionparseCallNode: import TSESTreeTSESTree.CallExpression, schemaNode: TSESTree.NodeschemaNode: import TSESTreeTSESTree.type Node = TSESTree.CallExpression | TSESTree.AccessorProperty | TSESTree.ArrayExpression | TSESTree.ArrayPattern | TSESTree.ArrowFunctionExpression | TSESTree.AssignmentExpression | TSESTree.AssignmentPattern | TSESTree.AwaitExpression | TSESTree.BinaryExpression | TSESTree.BlockStatement | TSESTree.BreakStatement | TSESTree.CatchClause | TSESTree.ChainExpression | TSESTree.ClassBody | ... 153 more ... | TSESTree.YieldExpressionNode, argument: TSESTree.Nodeargument: import TSESTreeTSESTree.type Node = TSESTree.CallExpression | TSESTree.AccessorProperty | TSESTree.ArrayExpression | TSESTree.ArrayPattern | TSESTree.ArrowFunctionExpression | TSESTree.AssignmentExpression | TSESTree.AssignmentPattern | TSESTree.AwaitExpression | TSESTree.BinaryExpression | TSESTree.BlockStatement | TSESTree.BreakStatement | TSESTree.CatchClause | TSESTree.ChainExpression | TSESTree.ClassBody | ... 153 more ... | TSESTree.YieldExpressionNode, ): boolean { // Check if this parse call is inside a conditional that checks safeParse().success // Pattern: safeParse(x).success ? [parse(x)] : [] let let parent: TSESTree.Node | undefinedparent: import TSESTreeTSESTree.type Node = TSESTree.CallExpression | TSESTree.AccessorProperty | TSESTree.ArrayExpression | TSESTree.ArrayPattern | TSESTree.ArrowFunctionExpression | TSESTree.AssignmentExpression | TSESTree.AssignmentPattern | TSESTree.AwaitExpression | TSESTree.BinaryExpression | TSESTree.BlockStatement | TSESTree.BreakStatement | TSESTree.CatchClause | TSESTree.ChainExpression | TSESTree.ClassBody | ... 153 more ... | TSESTree.YieldExpressionNode | undefined = parseCallNode: TSESTree.CallExpressionparseCallNode.BaseNode.parent: TSESTree.Nodeparent; // Look up the tree to find a ConditionalExpression while (let parent: TSESTree.Node | undefinedparent !== var undefinedundefined) { if (let parent: TSESTree.Nodeparent.type: AST_NODE_TYPEStype === enum AST_NODE_TYPESAST_NODE_TYPES.function (enum member) AST_NODE_TYPES.ConditionalExpression = "ConditionalExpression"ConditionalExpression) { const const test: TSESTree.Expressiontest = let parent: TSESTree.ConditionalExpressionparent.ConditionalExpression.test: TSESTree.Expressiontest; // Check if the test is a .success member access if ( const test: TSESTree.Expressiontest.type: AST_NODE_TYPES.ArrayExpression | AST_NODE_TYPES.ArrayPattern | AST_NODE_TYPES.ArrowFunctionExpression | AST_NODE_TYPES.AssignmentExpression | AST_NODE_TYPES.AwaitExpression | AST_NODE_TYPES.BinaryExpression | AST_NODE_TYPES.CallExpression | AST_NODE_TYPES.ChainExpression | AST_NODE_TYPES.ClassExpression | AST_NODE_TYPES.ConditionalExpression | AST_NODE_TYPES.FunctionExpression | AST_NODE_TYPES.Identifier | AST_NODE_TYPES.ImportExpression | AST_NODE_TYPES.JSXElement | AST_NODE_TYPES.JSXFragment | AST_NODE_TYPES.Literal | ... 18 more ... | AST_NODE_TYPES.TSTypeAssertiontype === enum AST_NODE_TYPESAST_NODE_TYPES.function (enum member) AST_NODE_TYPES.MemberExpression = "MemberExpression"MemberExpression && const test: TSESTree.MemberExpressionComputedName | TSESTree.MemberExpressionNonComputedNametest.property: TSESTree.PrivateIdentifier | TSESTree.Expressionproperty.type: AST_NODE_TYPES.ArrayExpression | AST_NODE_TYPES.ArrayPattern | AST_NODE_TYPES.ArrowFunctionExpression | AST_NODE_TYPES.AssignmentExpression | AST_NODE_TYPES.AwaitExpression | AST_NODE_TYPES.BinaryExpression | AST_NODE_TYPES.CallExpression | AST_NODE_TYPES.ChainExpression | AST_NODE_TYPES.ClassExpression | AST_NODE_TYPES.ConditionalExpression | AST_NODE_TYPES.FunctionExpression | AST_NODE_TYPES.Identifier | AST_NODE_TYPES.ImportExpression | AST_NODE_TYPES.JSXElement | AST_NODE_TYPES.JSXFragment | AST_NODE_TYPES.Literal | ... 19 more ... | AST_NODE_TYPES.TSTypeAssertiontype === enum AST_NODE_TYPESAST_NODE_TYPES.function (enum member) AST_NODE_TYPES.Identifier = "Identifier"Identifier && const test: TSESTree.MemberExpressionComputedName | TSESTree.MemberExpressionNonComputedNametest.property: TSESTree.Identifierproperty.Identifier.name: stringname === "success" ) { // Check if the object is a safeParse call if ( const test: TSESTree.MemberExpressionComputedName | TSESTree.MemberExpressionNonComputedNametest.MemberExpressionBase.object: TSESTree.Expressionobject.type: AST_NODE_TYPES.ArrayExpression | AST_NODE_TYPES.ArrayPattern | AST_NODE_TYPES.ArrowFunctionExpression | AST_NODE_TYPES.AssignmentExpression | AST_NODE_TYPES.AwaitExpression | AST_NODE_TYPES.BinaryExpression | AST_NODE_TYPES.CallExpression | AST_NODE_TYPES.ChainExpression | AST_NODE_TYPES.ClassExpression | AST_NODE_TYPES.ConditionalExpression | AST_NODE_TYPES.FunctionExpression | AST_NODE_TYPES.Identifier | AST_NODE_TYPES.ImportExpression | AST_NODE_TYPES.JSXElement | AST_NODE_TYPES.JSXFragment | AST_NODE_TYPES.Literal | ... 18 more ... | AST_NODE_TYPES.TSTypeAssertiontype === enum AST_NODE_TYPESAST_NODE_TYPES.function (enum member) AST_NODE_TYPES.CallExpression = "CallExpression"CallExpression && const test: TSESTree.MemberExpressionComputedName | TSESTree.MemberExpressionNonComputedNametest.MemberExpressionBase.object: TSESTree.CallExpressionobject.CallExpression.callee: TSESTree.Expressioncallee.type: AST_NODE_TYPES.ArrayExpression | AST_NODE_TYPES.ArrayPattern | AST_NODE_TYPES.ArrowFunctionExpression | AST_NODE_TYPES.AssignmentExpression | AST_NODE_TYPES.AwaitExpression | AST_NODE_TYPES.BinaryExpression | AST_NODE_TYPES.CallExpression | AST_NODE_TYPES.ChainExpression | AST_NODE_TYPES.ClassExpression | AST_NODE_TYPES.ConditionalExpression | AST_NODE_TYPES.FunctionExpression | AST_NODE_TYPES.Identifier | AST_NODE_TYPES.ImportExpression | AST_NODE_TYPES.JSXElement | AST_NODE_TYPES.JSXFragment | AST_NODE_TYPES.Literal | ... 18 more ... | AST_NODE_TYPES.TSTypeAssertiontype === enum AST_NODE_TYPESAST_NODE_TYPES.function (enum member) AST_NODE_TYPES.MemberExpression = "MemberExpression"MemberExpression && const test: TSESTree.MemberExpressionComputedName | TSESTree.MemberExpressionNonComputedNametest.MemberExpressionBase.object: TSESTree.CallExpressionobject.CallExpression.callee: TSESTree.MemberExpressionComputedName | TSESTree.MemberExpressionNonComputedNamecallee.property: TSESTree.PrivateIdentifier | TSESTree.Expressionproperty.type: AST_NODE_TYPES.ArrayExpression | AST_NODE_TYPES.ArrayPattern | AST_NODE_TYPES.ArrowFunctionExpression | AST_NODE_TYPES.AssignmentExpression | AST_NODE_TYPES.AwaitExpression | AST_NODE_TYPES.BinaryExpression | AST_NODE_TYPES.CallExpression | AST_NODE_TYPES.ChainExpression | AST_NODE_TYPES.ClassExpression | AST_NODE_TYPES.ConditionalExpression | AST_NODE_TYPES.FunctionExpression | AST_NODE_TYPES.Identifier | AST_NODE_TYPES.ImportExpression | AST_NODE_TYPES.JSXElement | AST_NODE_TYPES.JSXFragment | AST_NODE_TYPES.Literal | ... 19 more ... | AST_NODE_TYPES.TSTypeAssertiontype === enum AST_NODE_TYPESAST_NODE_TYPES.function (enum member) AST_NODE_TYPES.Identifier = "Identifier"Identifier && const test: TSESTree.MemberExpressionComputedName | TSESTree.MemberExpressionNonComputedNametest.MemberExpressionBase.object: TSESTree.CallExpressionobject.CallExpression.callee: TSESTree.MemberExpressionComputedName | TSESTree.MemberExpressionNonComputedNamecallee.property: TSESTree.Identifierproperty.Identifier.name: stringname === "safeParse" ) { // Check if it's the same schema const const safeParseSchema: TSESTree.ExpressionsafeParseSchema = const test: TSESTree.MemberExpressionComputedName | TSESTree.MemberExpressionNonComputedNametest.MemberExpressionBase.object: TSESTree.CallExpressionobject.CallExpression.callee: TSESTree.MemberExpressionComputedName | TSESTree.MemberExpressionNonComputedNamecallee.MemberExpressionBase.object: TSESTree.Expressionobject; const const safeParseArg: TSESTree.CallExpressionArgumentsafeParseArg = const test: TSESTree.MemberExpressionComputedName | TSESTree.MemberExpressionNonComputedNametest.MemberExpressionBase.object: TSESTree.CallExpressionobject.CallExpression.arguments: TSESTree.CallExpressionArgument[]arguments[0]; // Compare schema and argument const const isSameSchema: booleanisSameSchema = context: Readonly<RuleContext<"redundantParse", []>>context.sourceCode: Readonly<SourceCode>
A SourceCode object that you can use to work with the source that was passed to ESLint.
sourceCode
.function getText(node?: TSESTree.Node | TSESTree.Token, beforeCount?: number, afterCount?: number): string
Gets the source code for the given node.
@paramnode The AST node to get the text for.@parambeforeCount The number of characters before the node to retrieve.@paramafterCount The number of characters after the node to retrieve.@returnsThe text representing the AST node.
getText
(const safeParseSchema: TSESTree.ExpressionsafeParseSchema) ===
context: Readonly<RuleContext<"redundantParse", []>>context.sourceCode: Readonly<SourceCode>
A SourceCode object that you can use to work with the source that was passed to ESLint.
sourceCode
.function getText(node?: TSESTree.Node | TSESTree.Token, beforeCount?: number, afterCount?: number): string
Gets the source code for the given node.
@paramnode The AST node to get the text for.@parambeforeCount The number of characters before the node to retrieve.@paramafterCount The number of characters after the node to retrieve.@returnsThe text representing the AST node.
getText
(schemaNode: TSESTree.NodeschemaNode);
const const isSameArg: booleanisSameArg = const safeParseArg: TSESTree.CallExpressionArgumentsafeParseArg !== var undefinedundefined && context: Readonly<RuleContext<"redundantParse", []>>context.sourceCode: Readonly<SourceCode>
A SourceCode object that you can use to work with the source that was passed to ESLint.
sourceCode
.function getText(node?: TSESTree.Node | TSESTree.Token, beforeCount?: number, afterCount?: number): string
Gets the source code for the given node.
@paramnode The AST node to get the text for.@parambeforeCount The number of characters before the node to retrieve.@paramafterCount The number of characters after the node to retrieve.@returnsThe text representing the AST node.
getText
(const safeParseArg: TSESTree.CallExpressionArgumentsafeParseArg) ===
context: Readonly<RuleContext<"redundantParse", []>>context.sourceCode: Readonly<SourceCode>
A SourceCode object that you can use to work with the source that was passed to ESLint.
sourceCode
.function getText(node?: TSESTree.Node | TSESTree.Token, beforeCount?: number, afterCount?: number): string
Gets the source code for the given node.
@paramnode The AST node to get the text for.@parambeforeCount The number of characters before the node to retrieve.@paramafterCount The number of characters after the node to retrieve.@returnsThe text representing the AST node.
getText
(argument: TSESTree.Nodeargument);
if (const isSameSchema: booleanisSameSchema && const isSameArg: booleanisSameArg) { return true; } } } } let parent: TSESTree.Node | undefinedparent = let parent: TSESTree.CallExpression | TSESTree.AccessorProperty | TSESTree.ArrayExpression | TSESTree.ArrayPattern | TSESTree.ArrowFunctionExpression | TSESTree.AssignmentExpression | TSESTree.AssignmentPattern | TSESTree.AwaitExpression | TSESTree.BinaryExpression | TSESTree.BlockStatement | TSESTree.BreakStatement | TSESTree.CatchClause | TSESTree.ChainExpression | TSESTree.ClassBody | ... 153 more ... | TSESTree.YieldExpressionparent.parent?: TSESTree.CallExpression | TSESTree.AccessorPropertyComputedName | TSESTree.AccessorPropertyNonComputedName | TSESTree.ArrayExpression | TSESTree.ArrayPattern | TSESTree.ArrowFunctionExpression | TSESTree.AssignmentExpression | TSESTree.AssignmentPattern | TSESTree.AwaitExpression | TSESTree.PrivateInExpression | TSESTree.SymmetricBinaryExpression | TSESTree.BlockStatement | TSESTree.BreakStatement | ... 196 more ... | undefined
@remarksThis never-used property exists only as a convenience for code that tries to access node parents repeatedly.
parent
;
} return false; } return { RuleListenerBaseSelectors.CallExpression?: RuleFunction<TSESTree.CallExpression> | undefinedCallExpression(node: TSESTree.CallExpressionnode) { // Check if this is a .parse() or .safeParse() call if ( node: TSESTree.CallExpressionnode.CallExpression.callee: TSESTree.Expressioncallee.type: AST_NODE_TYPES.ArrayExpression | AST_NODE_TYPES.ArrayPattern | AST_NODE_TYPES.ArrowFunctionExpression | AST_NODE_TYPES.AssignmentExpression | AST_NODE_TYPES.AwaitExpression | AST_NODE_TYPES.BinaryExpression | AST_NODE_TYPES.CallExpression | AST_NODE_TYPES.ChainExpression | AST_NODE_TYPES.ClassExpression | AST_NODE_TYPES.ConditionalExpression | AST_NODE_TYPES.FunctionExpression | AST_NODE_TYPES.Identifier | AST_NODE_TYPES.ImportExpression | AST_NODE_TYPES.JSXElement | AST_NODE_TYPES.JSXFragment | AST_NODE_TYPES.Literal | ... 18 more ... | AST_NODE_TYPES.TSTypeAssertiontype === enum AST_NODE_TYPESAST_NODE_TYPES.function (enum member) AST_NODE_TYPES.MemberExpression = "MemberExpression"MemberExpression && node: TSESTree.CallExpressionnode.CallExpression.callee: TSESTree.MemberExpressionComputedName | TSESTree.MemberExpressionNonComputedNamecallee.property: TSESTree.PrivateIdentifier | TSESTree.Expressionproperty.type: AST_NODE_TYPES.ArrayExpression | AST_NODE_TYPES.ArrayPattern | AST_NODE_TYPES.ArrowFunctionExpression | AST_NODE_TYPES.AssignmentExpression | AST_NODE_TYPES.AwaitExpression | AST_NODE_TYPES.BinaryExpression | AST_NODE_TYPES.CallExpression | AST_NODE_TYPES.ChainExpression | AST_NODE_TYPES.ClassExpression | AST_NODE_TYPES.ConditionalExpression | AST_NODE_TYPES.FunctionExpression | AST_NODE_TYPES.Identifier | AST_NODE_TYPES.ImportExpression | AST_NODE_TYPES.JSXElement | AST_NODE_TYPES.JSXFragment | AST_NODE_TYPES.Literal | ... 19 more ... | AST_NODE_TYPES.TSTypeAssertiontype === enum AST_NODE_TYPESAST_NODE_TYPES.function (enum member) AST_NODE_TYPES.Identifier = "Identifier"Identifier && (node: TSESTree.CallExpressionnode.CallExpression.callee: TSESTree.MemberExpressionComputedName | TSESTree.MemberExpressionNonComputedNamecallee.property: TSESTree.Identifierproperty.Identifier.name: stringname === "parse" || node: TSESTree.CallExpressionnode.CallExpression.callee: TSESTree.MemberExpressionComputedName | TSESTree.MemberExpressionNonComputedNamecallee.property: TSESTree.Identifierproperty.Identifier.name: stringname === "safeParse") && node: TSESTree.CallExpressionnode.CallExpression.arguments: TSESTree.CallExpressionArgument[]arguments.Array<CallExpressionArgument>.length: number
Gets or sets the length of the array. This is a number one higher than the highest index in the array.
length
> 0
) { const const schemaNode: TSESTree.ExpressionschemaNode = node: TSESTree.CallExpressionnode.CallExpression.callee: TSESTree.MemberExpressionComputedName | TSESTree.MemberExpressionNonComputedNamecallee.MemberExpressionBase.object: TSESTree.Expressionobject; const const argument: TSESTree.CallExpressionArgumentargument = node: TSESTree.CallExpressionnode.CallExpression.arguments: TSESTree.CallExpressionArgument[]arguments[0]; // TypeScript guard - argument should exist due to length check above if (!const argument: TSESTree.CallExpressionArgumentargument) { return; } // Check if the callee is a Zod schema if (!function (local function) isZodSchema(node: TSESTree.Node): booleanisZodSchema(const schemaNode: TSESTree.ExpressionschemaNode)) { return; } // Skip if this is only a safeParse call (we only want to check parse calls) if (node: TSESTree.CallExpressionnode.CallExpression.callee: TSESTree.MemberExpressionComputedName | TSESTree.MemberExpressionNonComputedNamecallee.property: TSESTree.Identifierproperty.Identifier.name: "parse" | "safeParse"name === "safeParse") { return; } try { // Get the input argument's type const const argTsNode: TSESTreeToTSNode<TSESTree.CallExpressionArgument>argTsNode = const services: ParserServicesWithTypeInformationservices.ParserServicesNodeMaps.esTreeNodeToTSNodeMap: ParserWeakMapESTreeToTSNode<TSESTree.Node>esTreeNodeToTSNodeMap.ParserWeakMapESTreeToTSNode<Node>.get<TSESTree.CallExpressionArgument>(key: TSESTree.CallExpressionArgument): TSESTreeToTSNode<TSESTree.CallExpressionArgument>get(const argument: TSESTree.CallExpressionArgumentargument); const const argType: TypeargType = const checker: TypeCheckerchecker.TypeChecker.getTypeAtLocation(node: Node): TypegetTypeAtLocation(const argTsNode: TSESTreeToTSNode<TSESTree.CallExpressionArgument>argTsNode); const const argTypeString: stringargTypeString = const checker: TypeCheckerchecker.TypeChecker.typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): stringtypeToString(const argType: TypeargType); // Skip if the argument is unknown or any - those SHOULD be validated if (function (local function) isUnknownOrAny(typeString: string): booleanisUnknownOrAny(const argTypeString: stringargTypeString)) { return; } // Get the return type of the entire parse() call expression const const parseCallTsNode: CallExpressionparseCallTsNode = const services: ParserServicesWithTypeInformationservices.ParserServicesNodeMaps.esTreeNodeToTSNodeMap: ParserWeakMapESTreeToTSNode<TSESTree.Node>esTreeNodeToTSNodeMap.ParserWeakMapESTreeToTSNode<Node>.get<TSESTree.CallExpression>(key: TSESTree.CallExpression): CallExpressionget(node: TSESTree.CallExpressionnode); const const parseReturnType: TypeparseReturnType = const checker: TypeCheckerchecker.TypeChecker.getTypeAtLocation(node: Node): TypegetTypeAtLocation(const parseCallTsNode: CallExpressionparseCallTsNode); const const parseReturnTypeString: stringparseReturnTypeString = const checker: TypeCheckerchecker.TypeChecker.typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): stringtypeToString(const parseReturnType: TypeparseReturnType); // If the argument type already matches the parse return type, it's redundant // This works for both primitive types and branded types if (const argTypeString: stringargTypeString === const parseReturnTypeString: stringparseReturnTypeString) { // Don't flag if this parse is part of a safeParse check pattern // Pattern: safeParse(x).success ? [parse(x)] : [] if (function (local function) isInSafeParseConditional(parseCallNode: TSESTree.CallExpression, schemaNode: TSESTree.Node, argument: TSESTree.Node): booleanisInSafeParseConditional(node: TSESTree.CallExpressionnode, const schemaNode: TSESTree.ExpressionschemaNode, const argument: TSESTree.CallExpressionArgumentargument)) { return; } context: Readonly<RuleContext<"redundantParse", []>>context.function report(descriptor: ReportDescriptor<"redundantParse">): void
Reports a problem in the code.
report
({
ReportDescriptorNodeOptionalLoc.node: TSESTree.Node | TSESTree.Token
The Node or AST Token which the report is being attached to
node
: node: TSESTree.CallExpressionnode.CallExpression.callee: TSESTree.MemberExpressionComputedName | TSESTree.MemberExpressionNonComputedNamecallee.property: TSESTree.Identifierproperty,
messageId: "redundantParse"messageId: "redundantParse", ReportDescriptorBase<MessageIds extends string>.data?: Readonly<Record<string, unknown>> | undefined
The parameters for the message string associated with `messageId`.
data
: {
valueName: stringvalueName: function (local function) getValueName(node: TSESTree.Node): stringgetValueName(const argument: TSESTree.CallExpressionArgumentargument), valueType: stringvalueType: const argTypeString: stringargTypeString, }, }); } } catch { // If we can't analyze the types, don't report return; } } }, }; }, });

More examples:

Unit and Integration Tests

Recent posts from blogs that I like

My fireside chat about agentic engineering at the Pragmatic Summit

via Simon Willison

A weekend with Misia: 1

After marrying her cousin, the couple entertained Proust, Mallarmé, Gide, Debussy, and were patrons of Monet, Renoir, Odilon Redon, Signac and Toulouse-Lautrec.

via The Eclectic Light Company

Big tech engineers need big egos

It’s a common position among software engineers that big egos have no place in tech1. This is understandable - we’ve all worked with some insufferably overconfident engineers who needed their egos checked - but I don’t think it’s correct. In fact, I don’t know if it’s possible to survive as a softwa...

via Sean Goedecke