type-fest
type-fest copied to clipboard
Add `Not<string, 'not me'>`
I was looking for a way to implement “any string that doesn’t start with a slash” with template literals, then I realized that it can be achieved with a generic Not
type:
type Unslashed = Not<string, `/${string}`>
const valid: Unslashed = 'home'
const invalid: Unslashed = '/home'
Is there anything like Not
?
Edit: related:
- https://github.com/microsoft/TypeScript/issues/49867
Upvote & Fund
- We're using Polar.sh so you can upvote and help fund this issue.
- The funding will be given to active contributors.
- Thank you in advance for helping prioritize & fund our backlog.
I think it could be useful.
If only TS had negated types: https://github.com/microsoft/TypeScript/pull/29317
Seen in https://stackoverflow.com/questions/66354585/typescript-type-for-any-string-other-than-a-specific-string-literal
I can't make it work as a reusable type:
type Not<Yes, Not> = Yes extends Not ? never : Yes;
type NotHello = Not<string, 'hello'>;
const notHello: NotHello = 'hello';
// No error because `NotHello === string`
It's possible that TypeScript "forgets" the negation too early
https://www.typescriptlang.org/play?#code/C4TwDgpgBAcg9sAPATQgZwDSwQPigXilTSggA9gIA7AExPmCgH4oqIA3CAJygC4j0AbgBQoSNmAAJCABsZcAhMRpgXAJZUA5lgDkAC1nydOEQGM4VFawTS5cfg1vzF+w3B2CgA
The generic-based works but it's buggy with variables:
type Not<Yes, Not> = Yes extends Not ? never : Yes;
type NotHello<S extends string> = Not<S, 'hello'>;
export const join = <S extends string>(
...parts: Array<NotHello<S> | number>
): string => parts.join(',');
join('hello'); // Error 🎉
join('hello', 'world'); // Error 🎉
let nonConstString = 'sup'
join('hello', nonConstString); // No error 😰
https://www.typescriptlang.org/play?ts=4.6.4#code/C4TwDgpgBAcg9sAPATQgZwDSwQPigXilTSggA9gIA7AExPmCgH4oqIA3CAJygC4j0AbgCwAKFCRswABIQANnLiIAyqQrU6UNMC4BLKgHM8hBiqwByABbzF5nCNFjyYOF0YBjOFW1QAVnH0CKBU1SloSbT1DHAAKMQBIADpksABDNzR+AEEuLlSQRAZZBSVlPAAfVgBXAFsAI24cMQBKfkj9AwI8NIzE-30Y8wxzZod+qkHrEpHBKAB6OagAUVzXKEAeDcBI-agxccmbOCGocwB3VzkaGfnFla41rZ3HUTkIRiovAGEvbWUdDqDzGgqmBzLsAhMrAcju8qF9vMBflEDKNrthSKseIBeDcADHtQIA
The generic-based works but it's buggy with variables
They needs to be as const
or types will be widened.
Checkout the following code on TS Playground:
type Not<Yes, Not> = Yes extends Not ? never : Yes;
type NotHello<S extends string> = Not<S, 'hello'>;
export const join = <S extends string>(
...parts: Array<NotHello<S> | number>
): string => parts.join(',');
join('hello'); // Error 🎉
join('hello', 'world'); // Error 🎉
let nonConstString = 'sup' as const
join('hello', nonConstString); // Error 🎉
Could microsoft/TypeScript#51865 help solve this issue?