I am using typescript to write some function and this is the one that TS accepts:
export const useSomething = () => {
const useStorage = <
Well, you are defining a parameter, and parameters need names. Otherwise, how would you refer to it? I mean, you are using newData
when calling setItem
. You can't simply use setItem(T)
, it would be the similar to using setItem(string)
, which most surely is not what you want.
And I say 'similar', because string
is a valid object in JavaScript, and you can pass it as parameter. T
, however can be any thing, probably just a type definition, and those types disappear during compilation.
I think the closest thing you can hope for to a canonical answer is in the GitHub issues microsoft/TypeScript#13152 and microsoft/TypeScript#3081. The gist of the situation is this:
Supporting parameter names in function types is useful as documentation for functions and methods in libraries. A function type (username: string, password: string) => void
is the same type as (arg0: string, arg1: string) => void
, but the former probably helps developers write code more than the latter does. So support for type-annotated parameter names in function types is intended, documented in the (increasingly outdated) TypeScript spec, and used in libraries all over the place. In the words of one of the language maintainers:
I ... believe that it helps with documentation. Having used Haskell in the capacity that I have, I will say that omitting parameter names ... doesn't help anybody when they're learning a new library. While there's something to be said about an easier syntax, types being the only form of documentation for people often makes it difficult to understand intent ...
Furthermore, where type annotations on named values are supported in TypeScript, the types can be omitted and they will be inferred by the compiler. Failure to infer anything useful results in inferences of any
. For example, in the following function:
function f(x, y): void { }
type F = typeof f;
// type F = (x: any, y: any) => void
the type of x
and y
is inferred as any
(and you get a nice error with the --noImplicitAny
compiler option). It's the same as the following annotated version:
function g(x: any, y: any): void { }
type G = typeof g;
// type G = (x: any, y: any) => void
Applying the same rule to the type signatures of f
and g
themselves leads to the following behavior:
type Fprime = (x, y) => void; // --noImplicitAny yells at you here
// type Fprime = (x: any, y: any) => void
type Gprime = (x: any, y: any) => void;
// type Gprime = (x: any, y: any) => void
So when you write (x, y) => void
, the compiler interprets x
and y
as names and not as types. Since the type can be omitted, parameter names cannot. I don't think anyone likes this, but it's been this way for so long that it's apparently used in libraries, so changing this would break real-world code. From the same comment quoted above:
I think it is too late to make this sort of change. Because of the current behavior, interpreting a single identifier as a type would be a breaking change.
So that's the sad answer to this question. Maybe if they could go back in time they would make it so that the parameter name is optional and the type is required, to align more closely with type theory notation, but for now this is what we're stuck with.
Hope that helps; good luck!
Playground link to code
Following type alias can be used to avoid explicit parameter names:
type F<A extends unknown[], R> = (...args: A) => R
const example1: F<[number, boolean], number> = (x, y) => y ? x : x + 1;
const example2: F<[number], string> = String;