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

TypeScript accepts wrong code inferring unknown #48624

Closed
rpeszek opened this issue Apr 9, 2022 · 6 comments
Closed

TypeScript accepts wrong code inferring unknown #48624

rpeszek opened this issue Apr 9, 2022 · 6 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@rpeszek
Copy link

rpeszek commented Apr 9, 2022

Bug Report

🔎 Search Terms

searched:
is:issue is:open label:Bug ts(2345)
is:issue is:open label:Docs ts(2345)

🕗 Version & Regression Information

Tested with 4.6.3 and 4.5.2.

Please keep and fill in the line that best applies:

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

⏯ Playground Link

Playground link with relevant code

💻 Code

Hi, I wanted to share these examples. I could not find a similar ticket.
This is a compilation issue.
I have encountered several cases where a nonsensical code compiles.
Most of these examples have something in common, they all have unknown somewhere
in the type.

For example this compiles:

//heper
const curry = <T1, T2, R>(
  fn: (ax: T1, bx: T2) => R
): ((a: T1) => (b: T2) => R) => { ... };

/**
 * computed type is:
 * const nonsense22: <T1, T2, R>(a: (ax: T1, bx: T2) => R) => (b: unknown) => (a: T1) => (b: T2) => R
 */
const nonsense22 = curry(curry);

It is important to note that unknown can easily hide behind a narrower type, e.g.

const unknownReplaced22: (
  a: (ax: string, bx: string) => string
) => (b: string) => (b: string) => (a: string) => (b: string) => string = curry(
  curry(curry)
);

Here is a file with more examples (some made playground upset):

https://github.com/rpeszek/typescript-issues/blob/master/src/AcceptingWrongCode/IncorrectlyAcceptingUnknown.ts

These examples are also described in my blog series about TS types (Part 1 and 4):

https://rpeszek.github.io/tags/TypeScript-Notes.html

🙁 Actual behavior

Nonsensical code compiles

🙂 Expected behavior

Nonsensical code should not compile

@RyanCavanaugh
Copy link
Member

This is all expected behavior; I don't see any demonstration of a defect here (aside: just calling something nonsense doesn't help me explain where the disconnect is)

  • T1 has one candidate (type parameter T1 from curry, let's call it T1')
  • T2 has zero candidates, so soundly infers unknown
  • R has one candidate, ((a: T1) => (b: T2) => R) from curry
  • The type parameters get lifted into the resulting signature

In the unknownReplaced22, there are concrete contextual candidates string for all the type parameters, so those take precedence over the unbound generics.

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Apr 11, 2022
@typescript-bot
Copy link
Collaborator

This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

@rpeszek
Copy link
Author

rpeszek commented Apr 15, 2022

curry expects one argument (which needs to be a function of 2 arguments) and returns it curried version.
So curry(curry) is iExpectAFunctionOf2Args(iHaveOnlyOneArgFn) situation.
Arity is off.

Linked examples contain also other compiling code like:

/**
 * computed type is:
 * const nonsense1: (a: unknown) => (b: unknown) => string
 */
 
const nonsense1 = curry(() => "test");

Allowing me to curry function without any arguments.

I guess TS allows to pass almost any function as function argument, irregardless of its arity by design.

declare function tstx(fn: (a: string) => number):number
//compiles
tstx(() => 1)

The parameter types for fn play documentation role only.
I did not realized that. This is IMO quite weak safety.

@RyanCavanaugh
Copy link
Member

Yes, this is addressed in the FAQ and is the intended behavior

@rpeszek
Copy link
Author

rpeszek commented Apr 16, 2022

I see
https://github.com/Microsoft/TypeScript/wiki/FAQ#why-are-functions-with-fewer-parameters-assignable-to-functions-that-take-more-parameters

"functions with fewer parameters assignable to functions that take more parameters"

Thank you for your explanation.

@rpeszek
Copy link
Author

rpeszek commented Apr 16, 2022

It should be noted that "functions with fewer parameters assignable to functions that take more parameters"
combined with #43187
can indeed lead to some quite interesting bugs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

3 participants