I am using typescript to write some function and this is the one that TS accepts:
export const useSomething = <T>() => {
const useStorage = <T>(key: string, initialData: T) : [T, (newData: T) => Promise<void>] => {
const [data, setState] = useState<T>(initialData);
const setData = async(newData: T) : Promise<void> => {
await storage.setItem<T>(key, newData);
};
return [data, setData];
}
};
But inithially I wanted to write the return type of useStorage
in this way:
[T, (T) => Promise<void>]
Why TypeScript wants me to write down the newData
name before the T
occurs?
Developers can use the optional parameter to declare parameters in function optional so that the requirement to pass the value to optional parameters gets eliminated.
Optional Parameters are parameters that can be specified, but are not required. This allows for functions that are more customizable, without requiring parameters that many users will not need.
The caller's arguments passed to the function's parameters do not have to have the same names.
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;
Also if you mostly deal with 1 argument functions, you may use following type (it is a bit shorter) :
type F1<A, R> = F<[A], R>
// or
type F1<A, R> = (arg: A) => R
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With