Repository: TypeScript
Issue: Proposal: Enhance String interface definition to support type inference for string literals
Labels: Suggestion,Awaiting More Feedback

### 🔍 Search Terms

string generic methods, string concat, checked domain literal types 

Related issues [#44268](https://github.com/microsoft/TypeScript/issues/44268)

### ✅ Viability Checklist

- [x] This wouldn't be a breaking change in existing TypeScript/JavaScript code
- [x] This wouldn't change the runtime behavior of existing JavaScript code
- [x] This could be implemented without emitting different JS based on the types of the expressions
- [x] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- [x] This isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
- [x] This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals

### ⭐ Suggestion

**Current Behavior:**
Currently, any manipulation on string other then assigning to literals and using `${templates}` doesn't infer the type from literal string. Basic toString and valueOf will lose type of literal string in the process.
**Desired Behavior:**
I propose enhancing the type definitions for String interface so that it can infer the exact type when used with string literals and templates. This would improve type safety and developer experience when working with string manipulation. At least valueOf, toString, toUpperCase, toLowerCase can be implemented without changing something other than the definition of String interface.

**Example of Current Issue:**

```typescript
const result = 'hello'.concat(' ', 'world'); // TypeScript infers 'string' instead of 'helloworld'
```

**Proposed Solution:**

Introduce a new type definition for `concat` that uses variadic tuple types to infer the correct concatenated string literal type:

```typescript
type Join<S extends string[], D extends string> = 
    S extends [] ? '' :
    S extends [infer First extends string, ...infer Rest extends string[]] ? 
        `${First}${Rest extends [] ? '' : D}${Join<Rest, D>}` : string;

interface String {
    concat<This extends string, S extends string[]>(this: This, ...strings: S): `${This}${Join<S, ''>}`;
}


const c = 'qwery'.concat("123", 'abcd')

```

**Benefits:**
- **Improved Type Inference:** Developers will get precise types when concatenating string literals.
- **Better Code Completion and Error Detection:** IDEs can provide better suggestions and catch more errors at compile-time.
- **Consistency:** Aligns with TypeScript's goal of providing accurate and useful type information.

**Potential Drawbacks:**
- **Complexity:** This might increase the complexity of TypeScript's type system for string operations.
- **Performance:** There could be an impact on type-checking performance for very complex string concatenations.
- **Error on reassign** There could be a problems when concat used in let variable initialization
```typescript
type Join<S extends string[], D extends string> = 
    S extends [] ? '' :
    S extends [infer First extends string, ...infer Rest extends string[]] ? 
        `${First}${Rest extends [] ? '' : D}${Join<Rest, D>}` : string;

interface String {
    concat<This extends string, S extends string[]>(this: This, ...strings: S): `${This}${Join<S, ''>}`;
    toString<This extends string>(this: This): This;
    toUpperCase<This extends string>(this: This): Uppercase<This>;
    toLowerCase<This extends string>(this: This): Lowercase<This>;
    valueOf<This extends string>(this: This): This;
}

let a = "123".concat("qwerty");

a = "something else"; // this will result in error if implemented as interface modification because of inferred type "123qwerty"
``` 

**Additional Context:**
This change would particularly benefit scenarios where string templates or literal string concatenation are heavily used, enhancing the robustness of TypeScript's type system in string manipulation contexts.

[Playground](https://www.typescriptlang.org/play/?#code/C4TwDgpgBAUg9gSwHYB4DKUIA9gSQEwGcpDgAnZAcwG0BdAGigBFMc8iTyqA+KAXigAoKCKgZsuAsTpQA-FADkCqAC5ho8WylRqyAGYQyUAGIIypVpI6kKSSowB0T-YagAlCBYntiNqnVo5IVEQqAADABIAb1NzYABfaI8vLQ4ZeSVVZkSo+GQUZOBGJm54sKy-OwBuQUFQSDEAGwQAYwg0YABDMmB0Sx9OW3soADl+7SQAVwBbACNDRgBBcesuOxkBOl4BdRFIqMXqBUa8SmAACwVaMpXifZGb3dD5NCeQlTFb8OiXI1jSHK-dyeBLlN6hEQvZptDrdXqFRgjRjUf5FKBOByLWi8cEQj5oGp1cDQNDQiAAUQIfW82kqw0p+C+UzmCygyxpHE6SBAGx0DDZLRaXzp-EUCm2wVE+0Ox1OFyuNw5d2iDLKuKCi0F6vxX32QNRgKQBiMhTVEIhUNaFKp6vNogRtrtUAZ9EdduoGMWjFRDDd5ulgpyBrCfpE3D9H01LUJ9WgAGFzt1Fr1NFZfGthgBJJkzeZkJbCjO8rai9SpgZ6o2uA0-KsmkFm0TyGUnOzywJKqCZ8HyVHgj4JpPwkGMTPIz3esykbFPD4AIjnMeJoxmhlaAAU4IQEMAEAA3CAoAAqy87zLzEv2J8gitSdwAtNFz4YbvIkBAD0YPteIFURLVkFwMg9E6NoxAzKAoieFo4CQFpOl6I9zgQYhOzpRhy1pItsQACguFDv2QwhHCcOlCHxABKD4ryInI8lQNBGCUUowhqEIWkTMhk2PIjCyGRhBzITdULvKBnzIbg8KIwiUMYDjumEgdOOEqioEE7ikNktTlK3bg2NEBCeJQviqAEnSRLTMTc0MST8PIqBNOIqB5KErclIUrdVPUxCiLMjzCD0p5gDgDohiMiyBjpWzpIcojVMc-SRGCgBVMBIDIONOkIQ9HJMuxooI2KUNU1L0vg7LwsCkJgoAGTgAB3QxMoq3K0IzAr7Mc1S6sashypyoiqtEPdOkaSYIAAeT0cK8soDqZMIeKiMSkgyRmtr+PAuEcxZfNnQIHa81FABGCAAE4LvmoqnNIOF8S6HpGHYWQPgZVTSStWEenQMkGXCxgGW4DCHuAcN4lqE5gGc0UFAAR16kAFAcGC4IQnC52OgAmABmOcmMWAAhOMmAUCjCRRix6oAfRc0VYzgPRnORzjkxw46ycECmoYgGnONFFoHEIMkcMxxhsY5rnMGmMBQHnOd+eZoc2YABmViXYIsGCwBAY6FeC0KqBw9WkE1uBtcxhWRrGya9CNmpJcmNLXAEAWUqdjKsogI3OY1qHGga52mdqgOPey72fZNqG9A4AR33q8ChnRgAWXGKIcfWM3DyXOlFOcAB1JiYAAOAB2ckC6YONlaTucHDRtWI8IOATgcf3KBwzoKIhiBubIMg4CMAQ5xTzHa5R+DgHR465y7wRDH7weoDnJvph75C7CpiBGmyxdakbqOEC3mOdDnJBOlXvGl4QfBL7nGDSDnQIsuc33CT0SY4N3WCoEoPBDAQ9oA9dx2AAIrjTICASk5AQAoFMEfWajAJpkHwK4Tsc5FhoDjPLAAPkvJg5JMFzkknoQ+jRGQfDgWQxgA8UFkA+Eg2hFFIJPDID3SYZAkBQBIUfRWXFJ5q3TnAUqTVPZG2RrBCeOFuFkMFsLdmjA5x8EvjQwwHNwb7ygLgCwAhpFEAcNMToYAcJSNIfgJhfBeC6PEajSec4RjnwgDPMmUAAD0LioC-GILGDRWjgAWx0aYwg+jDHGN0eY3g+xdHxHsavMIzi3FQHwHATwSAFBQyBBcaA3iNGsNhuNbRP8-5kAAWgIBVAwGGEgUgaBJij6EGoMrfk6DCFdyAA)

---

### 📃 Motivating Example

In TypeScript, while working with string literals, certain operations like concatenation or transformations (e.g., toUpperCase, toLowerCase) typically result in the loss of specific literal types, being inferred as a general string. This can lead to a loss of valuable type information, resulting in less strict compile-time checks and the need for manual type assertions or annotations.

Consider the following example:

```typescript

const basePath = "/api";
const usersPath = "/users";
const fullPath = basePath.concat(usersPath);  // Inferred as `string`
```
Here, despite knowing that basePath is "/api" and usersPath is "/users", TypeScript loses the literal type information after concatenation, inferring fullPath as string, rather than "/api/users". This loss of precision means we can't rely on TypeScript to enforce strict types when building paths or identifiers, leading to potential runtime errors.

### 💻 Use Cases

1. What do you want to use this for?
To get rid of boilerplate when transforming strings and ensure that result of transformation satisfies the constraints 
2. What shortcomings exist with current approaches?
Explicit type declaration and cast after manipulations
3. What workarounds are you using in the meantime?
Create a bunch of utils functions that work as a Proxy for calling built in methods