Skip to content

Arrow function parameters using an alias and intersection are expanded inappropriately in .d.ts #55575

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

Closed
webstrand opened this issue Aug 30, 2023 · 1 comment Β· Fixed by #55820
Assignees
Labels
Fix Available A PR has been opened for this issue Needs Investigation This issue needs a team member to investigate its status.

Comments

@webstrand
Copy link
Contributor

πŸ”Ž Search Terms

arrow function type alias parameters expansion intersection

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about declarations and aliases.

⏯ Playground Link

https://www.typescriptlang.org/play?ts=5.3.0-dev.20230830#code/JYWwDg9gTgLgBAbzgBShMBnOBfOAzNEOAIigFMBDAYxgFoMyAbMm4gbgCgOyAPSWOFQgA7DPADCAVzEQQAZSYsYARjgBeOAB4A8mBjARAGgB8ACh4AuFGkw69B4YfwVGDY3ABkibAEp17hGxuPmh4PElhGgc4KRl5RRoAJjt9IzNLa3QMFIcnPBc3T28-QKA

πŸ’» Code

import { Props } from "react-select";

export const CustomSelect1 = <Option,>(x: Props<Option, false> & {}) => {}
export function CustomSelect2<Option,>(x: Props<Option, false> & {}) {}

πŸ™ Actual behavior

The output .d.ts incorrectly expands the imported Props alias. Note that it has incorrectly resolved the default parameter.

import { Props } from "react-select";
export declare const CustomSelect1: <Option>(x: Omit<import("react-select/base/dist/react-select-base.cjs").PublicBaseSelectProps<Option, false, import("react-select").GroupBase<Option>>, "inputValue" | "menuIsOpen" | "onChange" | "onInputChange" | "onMenuOpen" | "onMenuClose" | "value"> & Partial<import("react-select/base/dist/react-select-base.cjs").PublicBaseSelectProps<Option, false, import("react-select").GroupBase<Option>>> & import("react-select/dist/declarations/src/useStateManager").StateManagerAdditionalProps<Option>) => void;
export declare function CustomSelect2<Option>(x: Props<Option, false> & {}): void;

πŸ™‚ Expected behavior

The imported type alias Props must not be expanded, if the upstream package changes its defaults this will cause confusion because dependencies relying on the defaults will not be able to use the new default.

import { Props } from "react-select";
export declare const CustomSelect1: <Option>(x: Props<Option, false> & {}) => void;
export declare function CustomSelect2<Option>(x: Props<Option, false> & {}): void;

Additional information about the issue

This problem became much worse with the advent of 4.2.3, with prior version of typescript only mistakenly expanding the default argument.

Additionally this causes difficult-to-reproduce issues producing errors like

The inferred type of 'CustomSelect' cannot be named without a reference to '../../../node_modules/react-select/dist/declarations/src/useStateManager'. This is likely not portable. A type annotation is necessary.ts(2742)

because the expanded alias uses types that aren't imported.

@Andarist
Copy link
Contributor

What is interesting here is that in the case of function declaration isSymbolAccessibleWorker isn't even called. This also means that serializeTypeForDeclaration isn't called (as that leads to tracking entity names and to checking isSymbolAccessibleWorker).

The variable statement gets transformed here:

case SyntaxKind.VariableStatement: {
return cleanup(transformVariableStatement(input));
}

and the function declaration doesn't go through a similar transformation:

case SyntaxKind.FunctionDeclaration: {
// Generators lose their generator-ness, excepting their return type
const clean = cleanup(factory.updateFunctionDeclaration(
input,
ensureModifiers(input),
/*asteriskToken*/ undefined,
input.name,
ensureTypeParams(input, input.typeParameters),
updateParamsList(input, input.parameters),
ensureType(input, input.type),
/*body*/ undefined,
));

It feels that there might also be other differences between those 2 and that both branches should get unified more when it comes to emitting signature declarations. It's probably also worth rechecking if there are similar discrepancies between class expressions and class declarations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Fix Available A PR has been opened for this issue Needs Investigation This issue needs a team member to investigate its status.
Projects
None yet
5 participants