Typescript: noImplicitReturnType [Suggestion]

Created on 10 May 2017  路  8Comments  路  Source: microsoft/TypeScript

We have noImplicitReturns which is great and helps tick up silly mistakes but it woud great to have noImplicitReturnType as well.

So what would noImplicitReturnType do?
Currently we can write a function like so:

function life () {
    return 42;
}
let x = life(); // Number

So all is great, TypeScript works out the return type nicely for us.

function life (x: boolean) {
    if (x) {
        return 'hi';
    } else {
        return 42;
    }
}
let y = life(true); // Number / String

This is where things start to go a little crazy. TypeScript records y as of type number | string which is of course acurate. But what if i wanted this function to only return strings?

We already have support for that with limited modifications

function life (x: boolean): string {
    if (x) {
        return 'hi';
    } else {
        return 42; // Type '42' is not assignable to type 'string'.
    }
}
let y = life(true);

And as a result TypeScript nicly throws an error.

So my suggestion is that in much the same way as with noImplicitAny and let enforcing strict typeing an argument is added in the form of noImplicitReturnType which for my first example (only a single return) wouldn't have any effect but for the second would throw an error instead of typing the function as number | string

In Discussion Suggestion

Most helpful comment

I worry that new developers will enable this feature long before they fully understand the nature and expressive power of TypeScript's type system.

More concretely I think that adopting this would very likely result in

  1. Increased use of any
  2. Failure to understand and take advantage of union and intersection types
  3. Reduced benefits of CFA
  4. Reduced use of generics
  5. General confusion

There are already many misconceptions about TypeScript which have lead to poor coding practices that result in a failure to take advantage of the language.

Here is an unfortunately typical example from a Stack Overflow question:

export class ListComponent {
  items: any[] = [{id: 1, value: 'item1'}, {id: 2, value: 'item2'}, {id: 3, value: 'item3'}];
  myModel: any = {id: this.items[1].id , value: this.items[1].value};
}

We can all agree that such code is _terrible_, but why was it written that way?

I really want to stress that the above pattern is common.

I think adding this feature would be a serious mistake.

All 8 comments

You might be interested in the typedef tslint rule.

Interesting, will set that up for now but i belive something like this should be in TS itself

I worry that new developers will enable this feature long before they fully understand the nature and expressive power of TypeScript's type system.

More concretely I think that adopting this would very likely result in

  1. Increased use of any
  2. Failure to understand and take advantage of union and intersection types
  3. Reduced benefits of CFA
  4. Reduced use of generics
  5. General confusion

There are already many misconceptions about TypeScript which have lead to poor coding practices that result in a failure to take advantage of the language.

Here is an unfortunately typical example from a Stack Overflow question:

export class ListComponent {
  items: any[] = [{id: 1, value: 'item1'}, {id: 2, value: 'item2'}, {id: 3, value: 'item3'}];
  myModel: any = {id: this.items[1].id , value: this.items[1].value};
}

We can all agree that such code is _terrible_, but why was it written that way?

I really want to stress that the above pattern is common.

I think adding this feature would be a serious mistake.

Another more pertinent example is code such as

function f(n: number) {
  return n % 2 === 0 ? 'even' : 'odd';
}

With the proposed feature, this would result in an error

Error: Return type not specified.

How likely is it that when responding to said error, the user will write
function f(n: number): 'even' | 'odd'?

Presumably a union of unit types from the same primitive family would not trigger this error.

I'm sympathetic because it's easy to do something like

function fn() {
  if (x) return g();
  // ...
  return g;
}

and then deal with an inscrutable error message about foo | () => foo somewhere else in the program. I argued in favor of not creating union types for function return types but will admit that in practice it hasn't been that much of a problem.

@RyanCavanaugh I can definitely see the reasoning behind wanting some kind of error there but, at the same time, it is very situational as foo | () => foo is a very useful and perfectly valid type in some situations.

My fear is that use of this flag will be premature and that if it is enabled in the first TypeScript code base which someone works on, it may never occur to them that they can even express such a type.

Also, I wonder how the flag would be classified. Would it fall under --strict? That doesn't feel right to me.

I'm also want such behavior to be present in typescript itself.

Reason for this is that function return type is an its contract and you can break it very easily by modifying function. Of course in case of some other code use such function directly, typescript will warn there, but not all code consumed in type-safe manner and still should provide stable contract.

Consider example of server-side controller, its return type is what client-side will receive, but methods of controller are called inside some third-party lib which doesn't care greatly what it gets as function result.

@Controller('foo')
export class FooController {
  @Get()
  bar() {
    return 5;
  }

  @Get()
  bar2(id: string) {  // <-- there should be error with proposed feature
    if (id === "secret") {
      return "some text";
    }
    return 0;
  }
}

So, in general proposed compiler flag should throw error when function returns not a single type so that developer could review changes to decide between contract breakage or fixing code.

Also, suggested name of flag does not consistent with its wanted behavior, developer should not specify return type explicitly in case of single inferred type.

@salterok that's basically what I was looking for, if a function only ever returns a number it's type should be taken as number without complaint it's only automatic union types that would be blocked

Basically do what noImplicitAny does for let x = y; but with functions, perhaps a better name would be something like noImplicitReturnUnion?

Was this page helpful?
0 / 5 - 0 ratings