Skip to content

modify String.split and Array.pop to fix a non-bug(!?) in top-repos pubkey/rxdb  #56323

Closed

Description

🔍 Search Terms

#52299 (closed)
#30406 (marked as design limitation)

"typescript issues Literal String should have literal length"
Turned up #52243 and #41160
but those are both more complicated proposals with string ranges.
This proposal only considers plain string literal, for which implementing literal length would straightforward.

✅ Viability Checklist

⭐ Suggestion

before:

type StringLength<S extends string> = S["length"];
type T1 = StringLength<string> // number
type T2 = StringLength<""> // number
type T3 = StringLength<"."> // number
type T3 = StringLength<`${TypeVariable}`> // number


interface String {
    split(separator: string | RegExp, limit?: number): string[];
}
interface Array<T> {
    pop(): undefined | T;
}

after:

type StringLength<S extends string> = S["length"];
type T1 = StringLength<string> // number
type T2 = StringLength<""> // 0
type T3 = StringLength<"."> // 1
type T3 = StringLength<`${TypeVariable}`> // number


interface String {
    split<S extends string>(separator: S): number extends S["length] ? string[] : S["length"] extends 0 ? string[] : [string, ...string[]]
    split(separator: string | RegExp, limit?: number): string[];
}
interface Array<T> {
    pop(): this["length"] extends 0 ? undefined : T;
}

📃 Motivating Example

In the source of submitting another pull, this error occurred in the bot driver top-repos job

[pubkey/rxdb](https://github.com/pubkey/rxdb)

8 of 10 projects failed to build with the old tsc and were ignored
[config/tsconfig.types.json](https://github.com/pubkey/rxdb/blob/dd946905bf67c8cc26973003d63fa803215a3ca2/config/tsconfig.types.json)

    error TS2322: Type 'string | undefined' is not assignable to type 'string'.
        [src/plugins/dev-mode/check-schema.ts#L432](https://github.com/pubkey/rxdb/blob/dd946905bf67c8cc26973003d63fa803215a3ca2/src/plugins/dev-mode/check-schema.ts#L432)

[tsconfig.json](https://github.com/pubkey/rxdb/blob/dd946905bf67c8cc26973003d63fa803215a3ca2/tsconfig.json)

    error TS2322: Type 'string | undefined' is not assignable to type 'string'.
        [src/plugins/dev-mode/check-schema.ts#L432](https://github.com/pubkey/rxdb/blob/dd946905bf67c8cc26973003d63fa803215a3ca2/src/plugins/dev-mode/check-schema.ts#L432)

which maybe means it was already an error, and which can be reproduced in 5.2.2 with

function checkField(fieldName: string){
    let lastPathPart = fieldName;
    if (fieldName.includes('.')) {
        const partParts = fieldName.split('.');
        lastPathPart = partParts.pop(); // error
    }
}
"".split('.') satisfies [string, ...string[]];  // error
"".split('.').pop() satisfies string; // error

💻 Use Cases

  1. What do you want to use this for?

Eliminating a top-repos non-bug (Non-bug because it is not expected to compile in 5.2.2),
and to address previous issues which were closed without considering the solution proposed here.

  1. What shortcomings exist with current approaches?

Using "String.split" with a non zero length literal string is a common case that case a narrower type is correct.

Additionally, "Array.pop" is sometimes used on tuples with length >0 , and in that case
a narrower type is correct.

They may be used in conjunction as shown.

The goal is a better user experience.

  1. What workarounds are you using in the meantime?

No reasonable workaround is possible unless string literals are given specific length
in the same way that tuples are.

Activity

  1. MartinJohns commented on Nov 6, 2023

    @MartinJohns
    Contributor

    pop(): this["length"] extends 0 ? undefined : T;

    The type of length for arrays is number. This check will not work as you expect, because number never extends 0. This seems to be a duplicate of #30406.

    split(separator: string | RegExp, limit?: number): [string, string[]];

    split does not return a tuple with two elements. You probably meant to write [string, ...string[]], but this would also be wrong as split() can return an empty array. This seems to be a duplicate of #49635.

  2. MartinJohns commented on Nov 6, 2023

    @MartinJohns
    Contributor

    You changed it to:

    split(separator: ""): string[];
    split(separator: string): [string, ...string[]];

    This will only work for the string literal "", but if you have an empty string in a string typed variable you will still get the wrong type. This is mentioned in the issue I linked.

  3. craigphicks commented on Nov 6, 2023

    @craigphicks
    Author

    You changed it to:

    split(separator: ""): string[];
    split(separator: string): [string, ...string[]];

    This will only work for the string literal "", but if you have an empty string in a string typed variable you will still get the wrong type.

    I closed this issue, but then on second thoughts I open it again. That because use of "" as a separator has a different semantics from a separator which is not empty - it splits the string into length 1 character strings.

    Therefore, generally speaking, your linked example would not use a variable that could possibly be the empty string.

    Conversely, when the semantic behavior desired is exactly to split the string into characters, it will generally always use the string literal. I'd bet greater than even odds the modification could be made without triggering runtime regressions.

  4. craigphicks commented on Nov 6, 2023

    @craigphicks
    Author

    pop(): this["length"] extends 0 ? undefined : T;

    The type of length for arrays is number. This check will not work as you expect, because number never extends 0.

    Tuples have length with a literal number.

  5. RyanCavanaugh commented on Nov 6, 2023

    @RyanCavanaugh
    Member

    But approximately no one calls pop on a tuple

  6. MartinJohns commented on Nov 6, 2023

    @MartinJohns
    Contributor

    will generally always use the string literal.

    Bold assumption you make for a suggested change to the standard library types.

  7. craigphicks commented on Nov 6, 2023

    @craigphicks
    Author

    This proposal has some problems - I'm going to close it. The motivation, however, remains.
    A improved approach may be possible. It's always worth considering how to improve TypeScript user experience.

  8. locked as resolved and limited conversation to collaborators on Oct 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Working as IntendedThe behavior described is the intended behavior; this is not a bug

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions