-
Notifications
You must be signed in to change notification settings - Fork 17
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
fix: handle non-literal strings #80
Conversation
src/internal/intrinsic.ts
Outdated
@@ -0,0 +1,15 @@ | |||
export type UncapitalizeSTS<T extends string> = string extends T |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately this file is necessary.
The built-in Uncapitalize/Lowercase/Uppercase/Capitalize types have what (IMO) is bad behavior
type CapitalizeSTS1 = Expect<Equal<CapitalizeSTS<'abc'>, 'Abc'>>
type CapitalizeSTS2 = Expect<Equal<CapitalizeSTS<string>, string>>
type bad1 = Expect<Equal<Capitalize<'abc'>, 'Abc'>>
type bad2 = Expect<Equal<Capitalize<string>, Capitalize<string>>>
// ^ WTF
The bad2
type test showcases the "bad behavior" around the built-in types.
Why doesn't Capitalize<string>
resolve to string
?
This unfortunate behavior affects everything downstream (ie: word casing and object keys). We need utilities that work on both string and literal types, so I had to replace the built-in casing types with new types that would work correctly for both string and literal type inputs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know the answer but that makes me feel better about our functions also not being prepared to handle loose types
src/internal/internals.test.ts
Outdated
Equal< | ||
Subject.Reject<['one', '', 'two', '', 'three'], ''>, | ||
['one', 'two', 'three'] | ||
> | ||
> | ||
// // TODO: fix |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay with leaving this one as unresolved for now, as Reject
is not exported
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, and also I'm ok about all tests in this file as they don't have runtime counterparts
type test3 = Expect<Equal<CharAt<'some nice string', number>, string>> | ||
|
||
// TODO: index greater than Length<T> | ||
// type test4 = Expect<Equal<CharAt<'some nice string', 100>, ''>> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is gonna return undefined which is equal to runtime
// type test4 = Expect<Equal<CharAt<'some nice string', 100>, ''>> | |
// type test4 = Expect<Equal<CharAt<'some nice string', 100>, undefined>> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
type testGetPositiveIndex1 = Expect< | ||
Equal<Math.GetPositiveIndex<'abc', -1>, 2> | ||
> | ||
type testGetPositiveIndex2 = Expect< |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would be ok with leaving these methods simpler since they are only used by us internally
src/internal/intrinsic.ts
Outdated
@@ -0,0 +1,15 @@ | |||
export type UncapitalizeSTS<T extends string> = string extends T |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know the answer but that makes me feel better about our functions also not being prepared to handle loose types
src/utils/uncapitalize.ts
Outdated
export function uncapitalize<T extends string>(str: T): Uncapitalize<T> { | ||
export function uncapitalize<T extends string>(str: T): UncapitalizeSTS<T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We will have to export these type utilities as well :/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can update (to add these exports) during lunch today -- let me know if you have a preference for names. I chose STS
as an arbitrary suffix, and I'm open to suggestions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@p9f any opinions here? Maybe we should leave the changes in these functions to another PR so we can discuss.
If we export these types we must think through, if we ship with different types we are gonna be breaking a concept of matching type and runtime.
I'm thinking if there's a way to make our type utilities behave more like those of TS and leave them unchanged?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we ship with different types we are gonna be breaking a concept of matching type and runtime.
i am not sure to understand this part :/ afaiu this whole PR is making the type more precise for non literal string - but where do we have an inconsistency between runtime and types?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think our types will work with string
types (eg: CamelCase<string>
) without fixing the bad behavior that TS built-in functions (eg: Uppercase
) have.
Any utility that uses a built-in func will have a return type like Uppercase<string>
instead of string
. This is weird. Also, we pipe types in a lot of places, so it will cause issues where the outputs/inputs don't align between piped types
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand it now.. I'll think through it.
Still not convinced the value is worth the added LOCs and complexity 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO it is a critical mistake to not handle string
-- and the intrinsic/built-in string utilities cannot do that.
Uncapitalize
docstring: "Convert first character of string literal type to lowercase"
This type is only meant to be used with literals. Same with the others.
Given a scenario where Length<string> // 1 Often times we are getting an incorrect literal type as the output. Possible solutions:
Regardless of the solution, I don't think that leaving the current behavior in place is acceptable. |
Description
Handle literal and string types differently:
string-ts
types handle string literal type inputs correctly, but return incorrect types if the input type isstring
string
type input