最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

TypeScript Fails to Prevent Type Safety Holes from `any` - Stack Overflow

programmeradmin4浏览0评论

I am developing a generic function using TypeScript. My function, exampleFunction, accepts multiple props, including data, preSelected, and onSelect.

✅ Problem I Solved By using three different NoInfer implementations, I successfully prevented TypeScript from defaulting all inferred types to any. Now, TData inside the generic component does not collapse into any when preSelected or onSelect is provided incorrectly.

❌ Remaining Issue However, when allowMultipleSelection: true is set, TypeScript does not throw an error, even if preSelected or onSelect are incorrectly typed. I want TypeScript to enforce stricter type checks in this case and throw an error.

TS Playground

interface BaseProps<TData> {
  data: TData[];
}

// Multiple selection mode returning the full item
interface MultiSelectModeTypeItem<TData> extends BaseProps<TData> {
  returnType: 'item';
  allowMultipleSelection: true;
  preSelected?: NoInfer<TData[]>; // StrictArray kullan
  onSelect?: (selectedItems: NoInfer<TData[]>) => void;
}

// Single selection mode returning the full item
interface SingleSelectModeTypeItem<TData> extends BaseProps<TData> {
  returnType: 'item';
  allowMultipleSelection: false;
  preSelected?: NoInfer<TData>;
  onSelect?: (selectedItem: NoInfer<TData>) => void;
}

// Union type for SymbolFilterProps
export type SymbolFilterProps<TData> =
  | MultiSelectModeTypeItem<TData>
  | SingleSelectModeTypeItem<TData>

// Example data type
type SymbolFilterParams = {
  symbol: string;
  symbolDesc: string;
};

// Example function to test type enforcement
function exampleFunction<TData>(props: SymbolFilterProps<TData>) {
  console.log(props);
}

// Test data
const data: SymbolFilterParams[] = [];

// Incorrect usage for multiple selection (should trigger TypeScript errors)
// Here, 'selectedSymbols' is typed as any[], which is incorrect.
const selectedSymbols = [];  // <--- Here is the issue, we are using `any[]` or dont

// A generic onSelect function with any[] or dont as parameter (should trigger error)
const onSelect = (selectedItems) => {
  console.log(selectedItems);
};

// --- Correct Usage Examples ---

// Multiple selection mode using full item (expected to throw errors if types are incorrect)
exampleFunction({
  returnType: 'item',
  allowMultipleSelection: true,
  data,
  onSelect,         // If 'onSelect' is incorrectly typed, TypeScript should error
  preSelected: selectedSymbols // This should also error if the type doesn't match
});


// Single selection mode using full item (should be correct if types match)
exampleFunction({
  returnType: 'item',
  allowMultipleSelection: false,
  data,
  onSelect,
  preSelected: selectedSymbols
});

I think this is the minimal reproducible example I could create to demonstrate the issue:

TS Playground

interface MultiSelectMode<T> {
  allowMultipleSelection: true;
  preSelected?: NoInfer<T>[];
  onSelect?: (selectedItems: NoInfer<T>[]) => void;
}
interface SingleSelectMode<T> {
  allowMultipleSelection: false;
  preSelected?: NoInfer<T>;
  onSelect?: (selectedItem: NoInfer<T>) => void;
}
type Data<T> = { data: T[] }
type Props<T> = (MultiSelectMode<T> | SingleSelectMode<T>) & Data<T>;
type DataProps = { symbolCode: string, symbolDesc: string };

const exampleFunc = <T,>(props: Props<T>) => { }
const selectedSymbols: any[] = [];
const onSelect = (selectedItems: any[]) => { };
const data: DataProps[] = []

exampleFunc({ data, allowMultipleSelection: true, onSelect, preSelected: selectedSymbols })

exampleFunc({ data, allowMultipleSelection: false, onSelect, preSelected: selectedSymbols })

Problem Clarification any[] Assignment Issue:

Here, selectedSymbols is being declared as any[]. Since TypeScript defaults untyped props to any[], it silently bypasses type checking and doesn’t enforce the correct type on preSelected or onSelect.

Expectation: I expect TypeScript to throw an error when any[] is passed, even in multiple selection mode (allowMultipleSelection: true).

Current Behavior: TypeScript does not enforce the type checks when allowMultipleSelection: true and allows any[] to propagate.

Expected Behavior: I want TypeScript to:

  • Throw an error if preSelected = [] is passed without a correct type.
  • Throw an error if onSelect = selectedItems => {} is used without specifying the correct type.
  • Ensure TypeScript does not infer TData = any in any scenario.

Goal:

The goal is to ensure that when allowMultipleSelection: true, TypeScript enforces stricter type checks on preSelected and onSelect, just like it does for single selection mode (allowMultipleSelection: false).

I am developing a generic function using TypeScript. My function, exampleFunction, accepts multiple props, including data, preSelected, and onSelect.

✅ Problem I Solved By using three different NoInfer implementations, I successfully prevented TypeScript from defaulting all inferred types to any. Now, TData inside the generic component does not collapse into any when preSelected or onSelect is provided incorrectly.

❌ Remaining Issue However, when allowMultipleSelection: true is set, TypeScript does not throw an error, even if preSelected or onSelect are incorrectly typed. I want TypeScript to enforce stricter type checks in this case and throw an error.

TS Playground

interface BaseProps<TData> {
  data: TData[];
}

// Multiple selection mode returning the full item
interface MultiSelectModeTypeItem<TData> extends BaseProps<TData> {
  returnType: 'item';
  allowMultipleSelection: true;
  preSelected?: NoInfer<TData[]>; // StrictArray kullan
  onSelect?: (selectedItems: NoInfer<TData[]>) => void;
}

// Single selection mode returning the full item
interface SingleSelectModeTypeItem<TData> extends BaseProps<TData> {
  returnType: 'item';
  allowMultipleSelection: false;
  preSelected?: NoInfer<TData>;
  onSelect?: (selectedItem: NoInfer<TData>) => void;
}

// Union type for SymbolFilterProps
export type SymbolFilterProps<TData> =
  | MultiSelectModeTypeItem<TData>
  | SingleSelectModeTypeItem<TData>

// Example data type
type SymbolFilterParams = {
  symbol: string;
  symbolDesc: string;
};

// Example function to test type enforcement
function exampleFunction<TData>(props: SymbolFilterProps<TData>) {
  console.log(props);
}

// Test data
const data: SymbolFilterParams[] = [];

// Incorrect usage for multiple selection (should trigger TypeScript errors)
// Here, 'selectedSymbols' is typed as any[], which is incorrect.
const selectedSymbols = [];  // <--- Here is the issue, we are using `any[]` or dont

// A generic onSelect function with any[] or dont as parameter (should trigger error)
const onSelect = (selectedItems) => {
  console.log(selectedItems);
};

// --- Correct Usage Examples ---

// Multiple selection mode using full item (expected to throw errors if types are incorrect)
exampleFunction({
  returnType: 'item',
  allowMultipleSelection: true,
  data,
  onSelect,         // If 'onSelect' is incorrectly typed, TypeScript should error
  preSelected: selectedSymbols // This should also error if the type doesn't match
});


// Single selection mode using full item (should be correct if types match)
exampleFunction({
  returnType: 'item',
  allowMultipleSelection: false,
  data,
  onSelect,
  preSelected: selectedSymbols
});

I think this is the minimal reproducible example I could create to demonstrate the issue:

TS Playground

interface MultiSelectMode<T> {
  allowMultipleSelection: true;
  preSelected?: NoInfer<T>[];
  onSelect?: (selectedItems: NoInfer<T>[]) => void;
}
interface SingleSelectMode<T> {
  allowMultipleSelection: false;
  preSelected?: NoInfer<T>;
  onSelect?: (selectedItem: NoInfer<T>) => void;
}
type Data<T> = { data: T[] }
type Props<T> = (MultiSelectMode<T> | SingleSelectMode<T>) & Data<T>;
type DataProps = { symbolCode: string, symbolDesc: string };

const exampleFunc = <T,>(props: Props<T>) => { }
const selectedSymbols: any[] = [];
const onSelect = (selectedItems: any[]) => { };
const data: DataProps[] = []

exampleFunc({ data, allowMultipleSelection: true, onSelect, preSelected: selectedSymbols })

exampleFunc({ data, allowMultipleSelection: false, onSelect, preSelected: selectedSymbols })

Problem Clarification any[] Assignment Issue:

Here, selectedSymbols is being declared as any[]. Since TypeScript defaults untyped props to any[], it silently bypasses type checking and doesn’t enforce the correct type on preSelected or onSelect.

Expectation: I expect TypeScript to throw an error when any[] is passed, even in multiple selection mode (allowMultipleSelection: true).

Current Behavior: TypeScript does not enforce the type checks when allowMultipleSelection: true and allows any[] to propagate.

Expected Behavior: I want TypeScript to:

  • Throw an error if preSelected = [] is passed without a correct type.
  • Throw an error if onSelect = selectedItems => {} is used without specifying the correct type.
  • Ensure TypeScript does not infer TData = any in any scenario.

Goal:

The goal is to ensure that when allowMultipleSelection: true, TypeScript enforces stricter type checks on preSelected and onSelect, just like it does for single selection mode (allowMultipleSelection: false).

Share Improve this question edited Feb 15 at 22:44 Okay Beydanol asked Feb 15 at 10:56 Okay BeydanolOkay Beydanol 517 bronze badges 15
  • Thank you for your suggestion, @jcalz I've updated my example to remove framework dependencies, making it a pure TypeScript question. Now, it focuses solely on type inference and strict type checking for generics. Would appreciate your thoughts on whether the type enforcement is now correctly applied. TS Playground – Okay Beydanol Commented Feb 15 at 16:14
  • 1 Are you explicitly using any[] and then getting unhappy when it is assignable to string[]? But that's just how the any type works. It is intentionally unsafe, and if you use it, you're intentionally loosening the safety. No matter how you write your function, const foo: string[] = selectedSymbols will be allowed, so it's not even solvable in principle. If your issue is with any then maybe you want a linter rule to try to prevent any completely? Please edit to clarify and explain how we can proceed. – jcalz Commented Feb 15 at 16:40
  • @jcalz Thanks for your response! I understand that any[] is unsafe and allows assignment to string[]. My goal is to enforce strict type checks without requiring explicit types from the caller.The issue:When allowMultipleSelection:false, TypeScript correctly throws errors for incorrect preSelected, onSelect types.But when allowMultipleSelection:true, it doesn't enforce the same checks and allows any[] without errors.I used any[] intentionally to mimic real-world misuse. How can I ensure TypeScript enforces correct types in multiple selection mode just as it does in single selection mode?Thanks. – Okay Beydanol Commented Feb 15 at 16:54
  • 1 I don’t quite understand. How is it mimicking real world use? Where is the any[] coming from? Either this isn’t a minimal reproducible example or I just don’t understand your issue or both. Please edit the question to clarify. – jcalz Commented Feb 15 at 17:02
  • @jcalz I see the confusion.The any[] in my example represents a case where a developer does not explicitly type the preSelected or onSelect props when using the function.Since TypeScript defaults untyped props to any this can silently bypass type checks and lead to unexpected behavior.In single selection (allowMultipleSelect:false)TypeScript enforces strict types but in multiple (allowMultipleSelect:true)it does'n enforce the same checks allows any[].How can I enforce strict type checks in multiple selection mode just as I do in single selection mode even when no types are explicitly provided? – Okay Beydanol Commented Feb 15 at 18:08
 |  Show 10 more comments

1 Answer 1

Reset to default 1

I want TypeScript to enforce stricter type checks in this case and throw an error

That's not something reasonably easy to do in TypeScript alone - nor should it be. any and any[] are completely valid types in TypeScript. Their intended purpose is to let developers work with not-completely-typed data. They're intentional "escape hatches" for the type system.

To avoid anys in nuanced code, two strategies you'll find useful are:

  • Don't declare evolving or implicit anys
  • Lint to prevent accidental uses of anys

Don't declare evolving or implicit anys to begin with

Note that the two terms are different:

  • Evolving anys are when a value starts without an inferable type but can be narrowed over time: https://effectivetypescript/2020/03/09/evolving-any
  • Implicit anys are when a value can't have a better type inferred from its initial declaration and can't be evolved over time: https://www.totaltypescript/concepts/parameter-x-implicitly-has-an-any-type

In the snippets in the OP and the comments underneath it, you have two main examples of this:

  • Evolving any: the const selectedSymbols = [];
    • This one also counts as an implicit any because it's used in ways later on (an argument to a generic type parameter) that TypeScript can't reasonably infer a type from
  • Implicit any: the const onSelect = (selectedItems) => {

TypeScript's noImplicitAny compiler option is already reporting type errors on them in the playground. Good!

Lint to prevent accidental uses of anys

Even with noImplicitAny enabled, you may sometimes find cases of the any type getting introduced into code. Lint rules such as those in typescript-eslint's recommended config and recommendedTypeChecked config can catch those.

A couple that are commonly useful:

  • @typescript-eslint/no-explicit-any: reports on explicit uses of the any type
  • @typescript-eslint/no-unsafe-argument: reports on passing an any-typed value as an argument to a parameter that doesn't accept any

See https://typescript-eslint.io/blog/avoiding-anys for more avoiding anys with typescript-eslint.

发布评论

评论列表(0)

  1. 暂无评论