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

Operator '+' cannot be applied to types 'T' and 'T'. #12410

Closed
Jameskmonger opened this issue Nov 21, 2016 · 15 comments
Closed

Operator '+' cannot be applied to types 'T' and 'T'. #12410

Jameskmonger opened this issue Nov 21, 2016 · 15 comments
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue Help Wanted You can do this

Comments

@Jameskmonger
Copy link

TypeScript Version: 2.1.1 / nightly (2.2.0-dev.201xxxxx)

Code

function add<T extends string | number>(x: T, y: T): T {
  return x + y;
}

add<string>("a", "b");
add<number>(5, 3);
add("a", "b");
add(5, 3);

Expected behavior:

Code to compile fine, because T is of type string | number and therefore can have the + operator applied.

Actual behavior:

Operator '+' cannot be applied to types 'T' and 'T'.

@mhegazy mhegazy added Help Wanted You can do this Bug A bug in TypeScript labels Dec 16, 2016
@mhegazy mhegazy added this to the Community milestone Dec 16, 2016
@arusakov
Copy link
Contributor

Also doesn't work correctly for simple union type:

type X = number | string;

function add(a: X, b: X) {
    return a + b; // Error: Operator '+' cannot be applied to types 'X' and 'X'.
}

@DanielRosenwasser DanielRosenwasser added the Good First Issue Well scoped, documented and has the green light label Dec 20, 2016
@DanielRosenwasser
Copy link
Member

For those who want to tackle this, you might need to use getConstraintOfTypeParameter.

@arusakov
Copy link
Contributor

arusakov commented Dec 21, 2016

@DanielRosenwasser

type U = string | number;
declare const x: U;
declare const y: U;
declare const s: string;
declare const n: number;

x + y; // resultType is U
x + s; // resultType is string
x + n; // resultType is U

Am I right?

@Jameskmonger
Copy link
Author

Jameskmonger commented Dec 21, 2016

@mhegazy I noticed that your comment in #13073 said this should be relaxed for x + y when x and y are both string | number, but I'm not sure that aligns correctly with the behaviour in Chrome.

function SomeClass (val) {
    this.val = val;
}

SomeClass.prototype.toString = function () {
    return `value [${this.val}]`;
};

var x = new SomeClass(3);

var z = (x + x);
console.log(z); // "value [3]value [3]"

@mhegazy mhegazy added In Discussion Not yet reached consensus and removed Help Wanted You can do this Good First Issue Well scoped, documented and has the green light labels Dec 21, 2016
@mhegazy
Copy link
Contributor

mhegazy commented Dec 21, 2016

looking at this again, i do not think this is as simple as i originally thought. we need to talk about the new rules for the operator.

@mhegazy mhegazy removed this from the Community milestone Dec 21, 2016
@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Dec 21, 2016

type U = string | number;
declare const x: U;
declare const y: U;
declare const s: string;
declare const n: number;

x + y; // resultType is U
x + s; // resultType is string
x + n; // resultType is U

@arusakov To expand that out:

x + y; // resultType is `string | number`
x + s; // resultType is `string`
x + n; // resultType is `string | number`

that looks correct. However, if U was a type parameter whose constraint was string | number, I don't think necessarily think U would stick around.

@mhegazy
Copy link
Contributor

mhegazy commented Dec 22, 2016

what about U + T where both are constrained to string | number.

@Jameskmonger
Copy link
Author

I think it should be string | number | T where T is any object with a toString method.

@DanielRosenwasser
Copy link
Member

You don't know specifically whether each was a number or a string - so it sounds like that would become string | number unless you wanted to create a delayed type construct like T + U which only flattens when it has concrete types on both sides, but I think that seems a little overboard.

@RyanCavanaugh RyanCavanaugh added Help Wanted You can do this and removed In Discussion Not yet reached consensus labels Mar 14, 2017
@RyanCavanaugh
Copy link
Member

Accepting PRs for the rules:

  • T + T where T is constrained to string should return string (not T)
  • T + T where T is constrained to number should return number (not T)
  • Same rule above applies for all the other math binary and unary operators

T + T where T extends string | number is still disallowed -- we don't feel this is a good use case because whether you get concatenation or addition is not predictable, thus something that people shouldn't be doing.

@Jameskmonger
Copy link
Author

@RyanCavanaugh surely if T extends string | number and you perform T + T you will either be doing string + string or number + number depending on the value of T, thus making it predictable?

For example in my example I know that whatever I use as the generic type T will be the basis for the + operator's behaviour.

@RyanCavanaugh
Copy link
Member

@Jameskmonger if T extends string | number then T can be string | number which means one value of type T can be string a different value of type T can be a number.

It really sounds like you should use overloads here rather than a type parameter.

@Jameskmonger
Copy link
Author

Ah of course @RyanCavanaugh - I was misunderstanding how generic types work with union types. Thanks for explaining 😄

@mhegazy mhegazy added this to the Community milestone Jan 4, 2018
@jack-williams
Copy link
Collaborator

Can this be closed? I think the rules @RyanCavanaugh gave are currently satisfied:

function addString<T extends string>(x: T, y: T): string {
  return x + y;
}

function addNum<T extends number>(x: T, y: T): number {
  let a = x * y;
  let b = x - y;
  let z = x++;
  return x + y;
}

addString("a", "b");
addNum(1, 2);

Playground

@mhegazy mhegazy added the Fixed A PR has been merged for this issue label Feb 12, 2018
@mhegazy mhegazy removed this from the Community milestone Feb 12, 2018
@mhegazy mhegazy added this to the TypeScript 2.7 milestone Feb 12, 2018
@mhegazy
Copy link
Contributor

mhegazy commented Feb 12, 2018

thanks!

@mhegazy mhegazy closed this as completed Feb 12, 2018
@microsoft microsoft locked and limited conversation to collaborators Jul 3, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue Help Wanted You can do this
Projects
None yet
Development

No branches or pull requests

6 participants