-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Allow using types defined inside function body in the function's signature #61245
Comments
Duplicate of #8704. What would the declaration file for this even look like? Functions don't contain an implementation anymore, but you can't just pull up the types to the outer scope. |
Putting backwards compat stuff aside, you could extend the declration file syntax if really needed π: // .d.ts
function myFunction<T1, T2>(param1: TParam1, param2: TParam2): TReturn {
type TParam1 = CalculateParam1Type<T1, T2>;
type TParam2 = CalculateParam2Type<T1, T2>;
type TReturn = CalculateReturnType<T1, T2>;
// return result; β anything other than type declaration is error inside declaration file function body
} |
Yes, declaration files are a challenge. If there was a way to do it in DTS files without boilerplate, we would also have a way to do it in TS files. So this feature needs to be added to declarations too somehow. The way @uhyo proposed looks good to me. It's a syntax change though, so everyone may not be happy about that. |
If we go into the syntax change direction, there are other solutions that may make sense. To avoid this issue, I sometimes use tricks like this: function myFunction<
T1,
T2,
TParam1 extends CalculateParam1Type<T1, T2> = any,
TParam2 extends CalculateParam2Type<T1, T2> = any,
TReturn extends CalculateReturnType<T1, T2> = any
>(param1: TParam1, param2: TParam2): TReturn {
return {} as TReturn;
} This is not ideal because, (1) it makes signature more complex, (2) caller may change derived types by mistake, (3) it feels like a hack instead of an intended feature. If we can add a modifier like function myFunction<
T1,
T2,
derived TParam1 extends CalculateParam1Type<T1, T2>,
derived TParam2 extends CalculateParam2Type<T1, T2>,
derived TReturn extends CalculateReturnType<T1, T2>
>(param1: TParam1, param2: TParam2): TReturn {
return {} as TReturn;
} |
This would also be a breaking change, since currently this is legal and means something different: type Foo = number;
function f(x: Foo) {
type Foo = string;
} The existing suggestion to make unspecified type arguments behave differently seems like the right way to try to fix this. |
I guess you are referring to one of #10571 or #26242 which I am also looking forward to. Fixing those will make new patterns emerge. There may not be need to fix this issue specifically then, as there will be more workarounds. It was still worth a shot to open this issue. This can be closed as far as I am concerned. |
By the way, here is a workaround that at least helps you not write the same type twice: function myFunction<T1, T2>(param1: CalculateParam1Type<T1,T2>, param2: CalculateParam2Type<T1, T2>): CalculateReturnType<T1, T2> {
type TParam1 = typeof param1;
type TParam2 = typeof param2;
type TReturn = ReturnType<typeof myFunction<T1, T2>>;
const result: TReturn = { /* ... */ };
return result;
} |
function myFunction<T1, T2, T3, T4>(param1: CalculateParam1Type<CalculateAnotherType<T1,T2,T3, T4>>, param2: CalculateParam2Type<T1, T2>): CalculateReturnType<T1, T2> {
type TParam1 = typeof param1;
type TParam2 = typeof param2;
type TReturn = ReturnType<typeof myFunction<T1, T2>>;
type TTypeINeed = CalculateAnotherType<T1,T2,T3,T4>; // <-- This
const result: TReturn = { /* ... */ };
return result;
} So unless there is a way to use the generic type parameters before the function signature, there is always a possible use case where you need to repeat code. |
This issue has been marked as "Declined" and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
π Search Terms
"type alias in function signature", "type alias in function body", "type scope", "duplicate types", "hoist type alias"
β Viability Checklist
β Suggestion
This is a weird request, and I am certain it's not going to be accepted easily. I am basically asking to break lexical scoping for this use case. But I want to show the pain point, so that maybe a better solution can be found.
My suggestion is to hoist type declarations at the top of a function body to outside of the function, so that those type declarations can be used in the function signature.
π Motivating Example
Suppose a function like this:
Notice how I wrote the same types multiple times. It's often worse than that when you have more generic type parameters, and type parameters are named descriptively like
TMyValue
instead the shortT1
etc. The code becomes unreadable quickly. To reduce code duplication, you often have to write intermediate generic types that are only used for that function, and still pass the long list of generics to that type each time.Typescript should allow us to write the function above like:
This can be a breaking change, as
TParam1
could exist outside of this function for example. In such a case, the type declaration that is outside should be picked to not cause a breaking change.π» Use Cases
Generic functions with complex types would benefit from this change. Library level types often need to handle many cases at once and make invalid states irrepresentable. That often requires complex types, and it goes out of hand without a feature like this.
Current approach is to duplicate the type everywhere it's needed. Duplicate code can lead to bugs. Also code becomes unreadable with large types.
Defining each type declaration multiple times.
The text was updated successfully, but these errors were encountered: