-
-
Notifications
You must be signed in to change notification settings - Fork 625
IsLiteral
: Adding strictStringCheck
option
#1153
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
base: main
Are you sure you want to change the base?
Changes from 1 commit
55557e1
650d78b
0887bf6
dc1bd3e
4e41c40
0d87da2
16424a5
1cab4c2
1234b40
8b9b397
68ee458
03a3634
8b844df
934efc4
9d99a63
859c4e0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,22 @@ | ||
import type {ApplyDefaultOptions, CollapseLiterals} from './internal/object.d.ts'; | ||
import type {Extends, IsNotFalse, IsTrue, Not} from './internal/type.d.ts'; | ||
import type {TagContainer, UnwrapTagged} from './tagged.d.ts'; | ||
import type {Primitive} from './primitive.d.ts'; | ||
import type {Numeric} from './numeric.d.ts'; | ||
import type {CollapseLiterals, IfNotAnyOrNever, IsNotFalse, IsPrimitive} from './internal/index.d.ts'; | ||
import type {IsNever} from './is-never.d.ts'; | ||
import type {TagContainer, UnwrapTagged} from './tagged.d.ts'; | ||
import type {Numeric} from './numeric.d.ts'; | ||
import type {IsAny} from './is-any.d.ts'; | ||
import type {And} from './and.d.ts'; | ||
|
||
/** | ||
@see {@link IsLiteral} | ||
*/ | ||
type IsLiteralOptions = { | ||
strict?: boolean; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I was thinking of referencing the type IsStringLiteralOptions = {
strict?: boolean;
};
/**
Supports all {@link IsStringLiteralOptions} options.
*/
type IsLiteralOptions = IsStringLiteralOptions; In future if we add options to any other /**
Supports all {@link IsStringLiteralOptions} and {@link IsNumberLiteralOptions} options.
*/
type IsLiteralOptions = IsStringLiteralOptions & IsNumberLiteralOptions; Only downside is that we would have ensure there's no conflicting names. @sindresorhus WDYT? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That sound amazing actualy. great thinking ! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I Did it, and test it with and other options and worked flawlessly There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @sindresorhus @som-sm Hey guys what should we keep the current
|
||
}; | ||
|
||
type DefaultIsLiteralOptions = { | ||
strict: true; | ||
}; | ||
|
||
/** | ||
Returns a boolean for whether the given type `T` is the specified `LiteralType`. | ||
|
@@ -24,11 +38,10 @@ LiteralCheck<1, string> | |
type LiteralCheck<T, LiteralType extends Primitive> = ( | ||
IsNever<T> extends false // Must be wider than `never` | ||
? [T] extends [LiteralType & infer U] // Remove any branding | ||
? [U] extends [LiteralType] // Must be narrower than `LiteralType` | ||
? [LiteralType] extends [U] // Cannot be wider than `LiteralType` | ||
? false | ||
: true | ||
: false | ||
? And< | ||
Extends<U, LiteralType>, // Must be narrower than `LiteralType` | ||
Not<Extends<LiteralType, U>> // Cannot be wider than `LiteralType` | ||
> | ||
: false | ||
: false | ||
); | ||
|
@@ -47,12 +60,16 @@ LiteralChecks<1n, Numeric> | |
LiteralChecks<bigint, Numeric> | ||
//=> false | ||
``` | ||
|
||
@deprecated | ||
|
||
*/ | ||
type LiteralChecks<T, LiteralUnionType> = ( | ||
// Conditional type to force union distribution. | ||
// If `T` is none of the literal types in the union `LiteralUnionType`, then `LiteralCheck<T, LiteralType>` will evaluate to `false` for the whole union. | ||
// If `T` is one of the literal types in the union, it will evaluate to `boolean` (i.e. `true | false`) | ||
IsNotFalse<LiteralUnionType extends Primitive | ||
IsNotFalse< | ||
LiteralUnionType extends Primitive | ||
? LiteralCheck<T, LiteralUnionType> | ||
: never | ||
> | ||
|
@@ -111,21 +128,31 @@ type L2 = Length<`${number}`>; | |
//=> number | ||
``` | ||
|
||
@see IsStringPrimitive | ||
@category Type Guard | ||
@category Utilities | ||
*/ | ||
export type IsStringLiteral<S> = IfNotAnyOrNever<S, | ||
_IsStringLiteral<CollapseLiterals<S extends TagContainer<any> ? UnwrapTagged<S> : S>>, | ||
false, false>; | ||
|
||
export type _IsStringLiteral<S> = | ||
// If `T` is an infinite string type (e.g., `on${string}`), `Record<T, never>` produces an index signature, | ||
// and since `{}` extends index signatures, the result becomes `false`. | ||
S extends string | ||
? {} extends Record<S, never> | ||
? false | ||
: true | ||
: false; | ||
export type IsStringLiteral<T, Options extends IsLiteralOptions = {}> = ( | ||
benzaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
ApplyDefaultOptions<IsLiteralOptions, DefaultIsLiteralOptions, Options> extends infer ResolvedOptions extends Required<IsLiteralOptions> | ||
? IsNever<T> extends false | ||
? CollapseLiterals<T extends TagContainer<any> ? UnwrapTagged<T> : T> extends infer Type | ||
? ResolvedOptions['strict'] extends true | ||
? IsTrue<_IsStringLiteral<Type>> | ||
: LiteralCheck<Type, string> | ||
: never | ||
: false | ||
: never | ||
); | ||
|
||
type _IsStringLiteral<S> = ( | ||
// If `T` is an infinite string type (e.g., `on${string}`), `Record<T, never>` produces an index signature, | ||
// and since `{}` extends index signatures, the result becomes `false`. | ||
S extends string | ||
? {} extends Record<S, never> | ||
? false | ||
: true | ||
: false | ||
); | ||
|
||
/** | ||
Returns a boolean for whether the given type is a `number` or `bigint` [literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types). | ||
|
@@ -170,10 +197,17 @@ endsWith('abc123', end); | |
//=> boolean | ||
``` | ||
|
||
@see IsNumericPrimitive | ||
@category Type Guard | ||
@category Utilities | ||
*/ | ||
export type IsNumericLiteral<T> = LiteralChecks<T, Numeric>; | ||
export type IsNumericLiteral<T> = IsTrue< | ||
T extends number | ||
? T extends bigint | ||
? LiteralCheck<T, Numeric> | ||
: LiteralCheck<T, number> | ||
: LiteralCheck<T, bigint> | ||
>; | ||
|
||
/** | ||
Returns a boolean for whether the given type is a `true` or `false` [literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types). | ||
|
@@ -210,6 +244,7 @@ const eitherId = getId({asString: runtimeBoolean}); | |
//=> number | string | ||
``` | ||
|
||
@see IsBooleanPrimitive | ||
@category Type Guard | ||
@category Utilities | ||
*/ | ||
|
@@ -245,14 +280,15 @@ get({[symbolValue]: 1} as const, symbolValue); | |
//=> number | ||
``` | ||
|
||
@see IsSymbolPrimitive | ||
@category Type Guard | ||
@category Utilities | ||
*/ | ||
export type IsSymbolLiteral<T> = LiteralCheck<T, symbol>; | ||
|
||
/** Helper type for `IsLiteral`. */ | ||
type IsLiteralUnion<T> = | ||
| IsStringLiteral<T> | ||
type IsLiteralUnion<T, O extends IsLiteralOptions> = | ||
| IsStringLiteral<T, O> | ||
| IsNumericLiteral<T> | ||
| IsBooleanLiteral<T> | ||
| IsSymbolLiteral<T>; | ||
|
@@ -270,13 +306,13 @@ import type {IsLiteral} from 'type-fest'; | |
|
||
// https://github.com/inocan-group/inferred-types/blob/master/src/types/string-literals/StripLeading.ts | ||
export type StripLeading<A, B> = | ||
A extends string | ||
? B extends string | ||
? IsLiteral<A> extends true | ||
? string extends B ? never : A extends `${B & string}${infer After}` ? After : A | ||
: string | ||
: A | ||
: A; | ||
A extends string | ||
? B extends string | ||
? IsLiteral<A> extends true | ||
? string extends B ? never : A extends `${B & string}${infer After}` ? After : A | ||
: string | ||
: A | ||
: A; | ||
|
||
function stripLeading<Input extends string, Strip extends string>(input: Input, strip: Strip) { | ||
return input.replace(`^${strip}`, '') as StripLeading<Input, Strip>; | ||
|
@@ -291,10 +327,12 @@ stripLeading(str, 'abc'); | |
//=> string | ||
``` | ||
|
||
@see IsPrimitive | ||
@category Type Guard | ||
@category Utilities | ||
*/ | ||
export type IsLiteral<T> = | ||
IsPrimitive<T> extends true | ||
? IsNotFalse<IsLiteralUnion<T>> | ||
: false; | ||
export type IsLiteral<T, Options extends IsLiteralOptions = {}> = ( | ||
benzaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Extends<T, Primitive> extends true | ||
? IsNotFalse<IsLiteralUnion<T, Options>> | ||
: false | ||
); |
Uh oh!
There was an error while loading. Please reload this page.