Narrowing string to string literal union - string

I want to narrow a string to a string literal union. In other words, I want to check if the string is one of the possible values of my literal union, so that this will work (if the operator couldbe existed).
type lit = "A" | "B" | "C";
let uni: lit;
let str = "B";
if(str couldbe lit){
uni = str;
} else {
doSomething(str);
}
How can I achieve this?
I tried using if (str instanceof lit), but that doesn't seem to work. Using keyof to iterate over the string union doesn't work either, because the allowed values aren't keys per se.
One way would be to use switch with one case for each possible value, but that could lead to subtle errors if lits allowed values change.

If you hate switch cases, as I do:
since TypeScript 3.4 – const assertions it's also possible to produce union type from array of your strings ^_^
const list = <const>["A", "B", "C"];
type Lit = typeof list[number]; // "A" | "B" | "C"
function isLit(str: string): str is Lit {
return !!lits.find((lit) => str === lit);
}

You can use User-Defined Type Guards.
type lit = "A" | "B" | "C";
let uni: lit;
let str = "B";
function isLit(str: string): str is lit {
return str == "A" || str == "B" || str == "C";
}
function doSomething(str: string) {
}
if (isLit(str)) {
uni = str;
}
else {
doSomething(str);
}
ADD:
To avoid duplicated edit, class can be used both for compile-time and run-time. Now all you have to do is to edit just one place.
class Lit {
constructor(public A = 0, public B = 0, public C = 0) {}
}
type lit = keyof Lit;
let uni: lit;
function isLit(str: string): str is lit {
let lit = new Lit();
return (str in lit) ? true : false;
}

This is my take on the problem with the type guard and with strictNullChecks turned off (this is limitation on a project; if this option is true TS will require exhaustiveness on the switch/case).
Line const _notLit: never = maybeLit; guaranties that when you change lit type you need to update the switch/case also.
Downside of this solution is that it gets very verbose as the union type lit grows.
type lit = "A" | "B" | "C";
function isLit(str: string): str is lit {
const maybeLit = str as lit;
switch (maybeLit) {
case "A":
case "B":
case "C":
return true;
}
// assure exhaustiveness of the switch/case
const _notLit: never = maybeLit;
return false;
}
If possible this task is more suitable for enum or if you require a type and don't mind creating underlying enum for checking, you can create type guard something like this:
enum litEnum {
"A",
"B",
"C",
}
type lit = keyof typeof litEnum;
function isLit(str: string): str is lit {
return litEnum[str] !== undefined;
}

You can also use a zod enum to do this:
import zod from 'zod'
const ColorMode = zod.enum(['light', 'dark', 'system'] as const)
let _mode = 'light' // type is string
let mode = ColorMode.parse(_mode) // type is "light" | "dark" | "system"
_mode = 'twilight'
mode = ColorMode.parse(_mode) // throws an error, not a valid value
You can also extract the type from the zod schema when needed:
type ColorMode = zod.infer<typeof ColorMode>
I find a validation library like this is the easiest and most robust way to parse, validate, and type-narrow variables/data when I would otherwise have to reach for manually-written and error-prone type guards/predicates.

Related

How to Omit values for type definition from a set of values from Enum [duplicate]

Consider the following typescript enum:
enum MyEnum { A, B, C };
If I want another type that is the unioned strings of the keys of that enum, I can do the following:
type MyEnumKeysAsStrings = keyof typeof MyEnum; // "A" | "B" | "C"
This is very useful.
Now I want to create a generic type that operates universally on enums in this way, so that I can instead say:
type MyEnumKeysAsStrings = AnyEnumKeysAsStrings<MyEnum>;
I imagine the correct syntax for that would be:
type AnyEnumKeysAsStrings<TEnum> = keyof typeof TEnum; // TS Error: 'TEnum' only refers to a type, but is being used as a value here.
But that generates a compile error: "'TEnum' only refers to a type, but is being used as a value here."
This is unexpected and sad. I can incompletely work around it the following way by dropping the typeof from the right side of the declaration of the generic, and adding it to the type parameter in the declaration of the specific type:
type AnyEnumAsUntypedKeys<TEnum> = keyof TEnum;
type MyEnumKeysAsStrings = AnyEnumAsUntypedKeys<typeof MyEnum>; // works, but not kind to consumer. Ick.
I don't like this workaround though, because it means the consumer has to remember to do this icky specifying of typeof on the generic.
Is there any syntax that will allow me to specify the generic type as I initially want, to be kind to the consumer?
No, the consumer will need to use typeof MyEnum to refer to the object whose keys are A, B, and C.
LONG EXPLANATION AHEAD, SOME OF WHICH YOU PROBABLY ALREADY KNOW
As you are likely aware, TypeScript adds a static type system to JavaScript, and that type system gets erased when the code is transpiled. The syntax of TypeScript is such that some expressions and statements refer to values that exist at runtime, while other expressions and statements refer to types that exist only at design/compile time. Values have types, but they are not types themselves. Importantly, there are some places in the code where the compiler will expect a value and interpret the expression it finds as a value if possible, and other places where the compiler will expect a type and interpret the expression it finds as a type if possible.
The compiler does not care or get confused if it is possible for an expression to be interpreted as both a value and a type. It is perfectly happy, for instance, with the two flavors of null in the following code:
let maybeString: string | null = null;
The first instance of null is a type and the second is a value. It also has no problem with
let Foo = {a: 0};
type Foo = {b: string};
where the first Foo is a named value and the second Foo is a named type. Note that the type of the value Foo is {a: number}, while the type Foo is {b: string}. They are not the same.
Even the typeof operator leads a double life. The expression typeof x always expects x to be a value, but typeof x itself could be a value or type depending on the context:
let bar = {a: 0};
let TypeofBar = typeof bar; // the value "object"
type TypeofBar = typeof bar; // the type {a: number}
The line let TypeofBar = typeof bar; will make it through to the JavaScript, and it will use the JavaScript typeof operator at runtime and produce a string. But type TypeofBar = typeof bar; is erased, and it is using the TypeScript type query operator to examine the static type that TypeScript has assigned to the value named bar.
Now, most language constructs in TypeScript that introduce names create either a named value or a named type. Here are some introductions of named values:
const value1 = 1;
let value2 = 2;
var value3 = 3;
function value4() {}
And here are some introductions of named types:
interface Type1 {}
type Type2 = string;
But there are a few declarations which create both a named value and a named type, and, like Foo above, the type of the named value is not the named type. The big ones are class and enum:
class Class { public prop = 0; }
enum Enum { A, B }
Here, the type Class is the type of an instance of Class, while the value Class is the constructor object. And typeof Class is not Class:
const instance = new Class(); // value instance has type (Class)
// type (Class) is essentially the same as {prop: number};
const ctor = Class; // value ctor has type (typeof Class)
// type (typeof Class) is essentially the same as new() => Class;
And, the type Enum is the type of an element of the enumeration; a union of the types of each element. While the value Enum is an object whose keys are A and B, and whose properties are the elements of the enumeration. And typeof Enum is not Enum:
const element = Math.random() < 0.5 ? Enum.A : Enum.B;
// value element has type (Enum)
// type (Enum) is essentially the same as Enum.A | Enum.B
// which is a subtype of (0 | 1)
const enumObject = Enum;
// value enumObject has type (typeof Enum)
// type (typeof Enum) is essentially the same as {A: Enum.A; B: Enum.B}
// which is a subtype of {A:0, B:1}
Backing way way up to your question now. You want to invent a type operator that works like this:
type KeysOfEnum = EnumKeysAsStrings<Enum>; // "A" | "B"
where you put the type Enum in, and get the keys of the object Enum out. But as you see above, the type Enum is not the same as the object Enum. And unfortunately the type doesn't know anything about the value. It is sort of like saying this:
type KeysOfEnum = EnumKeysAsString<0 | 1>; // "A" | "B"
Clearly if you write it like that, you'd see that there's nothing you could do to the type 0 | 1 which would produce the type "A" | "B". To make it work, you'd need to pass it a type that knows about the mapping. And that type is typeof Enum...
type KeysOfEnum = EnumKeysAsStrings<typeof Enum>;
which is like
type KeysOfEnum = EnumKeysAsString<{A:0, B:1}>; // "A" | "B"
which is possible... if type EnumKeysAsString<T> = keyof T.
So you are stuck making the consumer specify typeof Enum. Are there workarounds? Well, you could maybe use something that does that a value, such as a function?
function enumKeysAsString<TEnum>(theEnum: TEnum): keyof TEnum {
// eliminate numeric keys
const keys = Object.keys(theEnum).filter(x =>
(+x)+"" !== x) as (keyof TEnum)[];
// return some random key
return keys[Math.floor(Math.random()*keys.length)];
}
Then you can call
const someKey = enumKeysAsString(Enum);
and the type of someKey will be "A" | "B". Yeah but then to use it as type you'd have to query it:
type KeysOfEnum = typeof someKey;
which forces you to use typeof again and is even more verbose than your solution, especially since you can't do this:
type KeysOfEnum = typeof enumKeysAsString(Enum); // error
Blegh. Sorry.
To recap:
This is not possible
Types and values blah blah
Still not possible
Sorry
It actually is possible.
enum MyEnum { A, B, C };
type ObjectWithValuesOfEnumAsKeys = { [key in MyEnum]: string };
const a: ObjectWithValuesOfEnumAsKeys = {
"0": "Hello",
"1": "world",
"2": "!",
};
const b: ObjectWithValuesOfEnumAsKeys = {
[MyEnum.A]: "Hello",
[MyEnum.B]: "world",
[MyEnum.C]: "!",
};
// Property '2' is missing in type '{ 0: string; 1: string; }' but required in type 'ObjectWithValuesOfEnumAsKeys'.
const c: ObjectWithValuesOfEnumAsKeys = { // Invalid! - Error here!
[MyEnum.A]: "Hello",
[MyEnum.B]: "world",
};
// Object literal may only specify known properties, and '6' does not exist in type 'ObjectWithValuesOfEnumAsKeys'.
const d: ObjectWithValuesOfEnumAsKeys = {
[MyEnum.A]: "Hello",
[MyEnum.B]: "world",
[MyEnum.C]: "!",
6: "!", // Invalid! - Error here!
};
Playground Link
EDIT: Lifted limitation!
enum MyEnum { A, B, C };
type enumValues = keyof typeof MyEnum;
type ObjectWithKeysOfEnumAsKeys = { [key in enumValues]: string };
const a: ObjectWithKeysOfEnumAsKeys = {
A: "Hello",
B: "world",
C: "!",
};
// Property 'C' is missing in type '{ 0: string; 1: string; }' but required in type 'ObjectWithValuesOfEnumAsKeys'.
const c: ObjectWithKeysOfEnumAsKeys = { // Invalid! - Error here!
A: "Hello",
B: "world",
};
// Object literal may only specify known properties, and '6' does not exist in type 'ObjectWithValuesOfEnumAsKeys'.
const d: ObjectWithKeysOfEnumAsKeys = {
A: "Hello",
B: "world",
C: "!",
D: "!", // Invalid! - Error here!
};
Playground Link
This work with const enum too!
There is a solution that doesn't require to create new generic types.
If you declare an enum
enum Season { Spring, Summer, Autumn, Winter };
To get to the type you only need to use the keywords keyof typeof
let seasonKey: keyof typeof Season;
Then the variable works as expected
seasonKey = "Autumn"; // is fine
// seasonKey = "AA" <= won't compile
You can just pass a type instead of a value and the compiler won't complain. This you achieve with typeof as you pointed out.
Will be just a bit less automatic:
type AnyEnumKeysAsStrings<TEnumType> = keyof TEnumType;
Which you can use as:
type MyEnumKeysAsStrings = AnyEnumKeysAsStrings<typeof MyEnum>;
If I understand the OP question correctly and Akxe answer: Here is a possible further simplification. Use the typescript type utility. Record<Keys, Type>
https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type
e.g.
enum MyEnum { A, B, C };
type enumValues = keyof typeof MyEnum;
type ObjectWithKeysOfEnumAsKeys = Record<enumValues, string>
const a: ObjectWithKeysOfEnumAsKeys = {
A: "PropertyA",
B: "PropertyB",
C: "PropertyC",
};

Generic function to get a nested object value

The problem itself is more complex than this but I'll try to explain in a simple way.
I have an object (it could be a different object, lets say a car o user or whatever).
const car = {
model: 'Model A',
specs: {
motor: {
turbo: true
}
}
};
Then I have a function to get a value from a property name.
interface Data<T, K extends keyof T> {
keynames: string[];
values: Array<T[K] | null>;
}
const getValues = <T, K extends keyof T>(keynames: string[], data: T): Data<T, K> => {
const values: Array<T[K] | null> = [];
keynames.forEach(keyname => {
const value: T[K] | undefined = getObjectValue(data, keyname);
if (typeof value === 'undefined') {
values.push(null);
} else {
values.push(value);
}
});
return {
keynames,
values
}
};
// This works with key.name syntax, specs.motor.turbo returns true for example
const getObjectValue = <T, K extends keyof T>(
object: T,
keyName: string
): T[K] | undefined => {
const keys = keyName.split('.');
if (keys.length === 1) {
if (typeof object === 'object') {
if (keys[0] in (object as T)) {
return (object as T)[keys[0] as K];
}
return undefined;
}
return undefined;
} else {
const [parentKey, ...restElements] = keys;
if (!object) return undefined;
return (getObjectValue(
(object as T)[parentKey as K],
restElements.join('.')
) as unknown) as T[K];
}
};
The problem becomes when I write some tests for example:
// ...
const data = getValues(['model', 'specs.motor.turbo'], car);
assert.deepEqual(data, {
keynames: ['model', 'specs.motor.turbo'],
values: ['Model A', true],
});
// ...
data is what I'm expecting, my function returns the same object as expected but I'm getting an error:
Type 'boolean' is not assignable to type 'string | { motor: { turbo: boolean; }; }'.ts(2322)
which is obvious since I'm only inferring T[K] in my function and not the type of the nested properties.
How to achieve that, so my function works with return types string | { motor: { turbo: boolean; }; } | { turbo: boolean; } | boolean. Or maybe there is a simpler way to return a nested property value.
I'm going to preserve your implementation as much as possible, even though there might be some improvements there that someone would suggest. I'm primarily taking this question as "how can I tell the TypeScript compiler what getValues() is doing?".
First, we need to represent what comes out when you deeply index into a type T with a dotted keypath K. This is only going to be possible in TypeScript 4.1 and later, since the following implementation relies both on template literal types as implemented in microsoft/TypeScript#40336, and recursive conditional types as implemented in microsoft/TypeScript#40002:
type DeepIndex<T, K extends string> = T extends object ? (
string extends K ? never :
K extends keyof T ? T[K] :
K extends `${infer F}.${infer R}` ? (F extends keyof T ?
DeepIndex<T[F], R> : never
) : never
) : never
The general plan here is to check if K is a key of T; if so, we do the lookup with T[K]. Otherwise we split K at the first dot into F and R, and recurse downward, indexing into T[F] with the key R. If at any point something goes wrong (F is not a valid key of T, for example), this returns never instead of property type. We will use this; if something becomes never then we assume that there was a bad index:
type ValidatePath<T, K> =
K extends string ? DeepIndex<T, K> extends never ? never : K : never;
The type ValidatePath<T, K> will extract from the (possible union of keys) K just those members which are valid paths.
Before we get into it, let's also represent what you do when you replace undefined with null:
type UndefinedToNull<T> = T extends undefined ? null : T;
For your code, I'm going to go ahead and use tuple types to represent the keynames and values arrays in Data. This should fall back to unordered arrays if necessary, but it seems weird to throw away information you know; for example, on your car example, you know that the first element of the values array is a string and the second is a boolean. A type like [string, boolean] has more information than Array<string | boolean | null>:
interface Data<T, K extends string[]> {
keynames: K;
values: { [I in keyof K]: UndefinedToNull<DeepIndex<T, Extract<K[I], string>>> };
}
The values property uses a mapped array/tuple to convert the tuple of keynames in K to a tuple of deep indexed values.
Now we need to give strong types to your function signatures. The compiler won't be able to verify a lot of the type safety inside of the implementations, so I'll be doing a lot of type assertions with as to force the compiler to accept what we're doing:
const getValues = <T, K extends string[]>(keynames: (K & { [I in keyof K]: ValidatePath<T, K[I]> }) | [], data: T): Data<T, K> => {
const values = [] as { [I in keyof K]: UndefinedToNull<DeepIndex<T, Extract<K[I], string>>> };
const _keynames = keynames as K;
_keynames.forEach(<I extends number>(keyname: K[I]) => {
const value: DeepIndex<T, Extract<K[I], string>> | undefined = getObjectValue(data, keyname as any);
if (typeof value === 'undefined') {
values.push(null as UndefinedToNull<DeepIndex<T, K[I]>>);
} else {
values.push(value as UndefinedToNull<DeepIndex<T, K[I]>>);
}
});
return {
keynames: _keynames,
values
}
};
const getObjectValue = <T, K extends string>(
object: T,
keyName: K & ValidatePath<T, K>
): DeepIndex<T, K> | undefined => {
const keys = keyName.split('.');
if (keys.length === 1) {
if (typeof object === 'object') {
if (keys[0] in (object as T)) {
return (object as T)[keys[0] as keyof T] as DeepIndex<T, K>;
}
return undefined;
}
return undefined;
} else {
const [parentKey, ...restElements] = keys;
if (!object) return undefined;
return (getObjectValue(
(object as T)[parentKey as keyof T],
restElements.join('.') as any as never
) as unknown) as DeepIndex<T, K>;
}
};
I wouldn't worry too much about the assertions inside the implementation. The important piece here is the call signature to getValues():
<T, K extends string[]>(keynames: (K & { [I in keyof K]: ValidatePath<T, K[I]> }) | [], data: T) => Data<T, K>
We are interpreting the keynames as something assignable to an array of string values. The | [] at the end is just a hint that the compiler should prefer viewing, say, ['model', 'specs.motor.turbo'] as an ordered pair instead of an unordered array. The intersection with {[I in keyof K]: ValidatePath<T, K[I]>}, a mapped tuple, should be a no-op is all the elements of K are valid paths into T. If any element of K is an invalid path, though, that element of the mapped tuple will be never and the validation will fail.
Let's test it:
const car = {
model: 'Model A',
specs: {
motor: {
turbo: true
}
}
}
const data = getValues(['model', 'specs.motor.turbo'], car);
data.keynames; // ["model", "specs.motor.turbo"]
data.values; // [string, boolean]
console.log(data);
This looks like exactly what you want with your example. What happens if we misspell a key?
getValues(['model', 'specs.motar.turbo'], car); // error!
// ---------------> ~~~~~~~~~~~~~~~~~~~
// Type 'string' is not assignable to type 'undefined' 🤷‍♂️
We get an error on the offending key. The error message isn't that useful, unfortunately. When I tried to use the answer to this question to give the compiler a full list of exactly which dotted paths to accept, it caused a massive slowdown and even some "type instantiation is excessively deep or infinite" errors. So while it would be nice to see "specs.motar.turbo" is not assignable to "model" | "specs" | "specs.motor" | "specs.motor.turbo"`, sadly I can't get that to happen in a reasonable way.
What if there could be an undefined value at the property in question?
const d2 = getValues(['a.b'], { a: Math.random() < 0.5 ? {} : { b: "hello" } });
d2.keynames; // ["a.b"]
d2.values; // [string | null]
console.log(d2);
It comes out as | null instead of | undefined, which is good.
So that works as well as I could get it to. There are undoubtedly limitations and edge cases. Deep indexing with dotted keys is kind of near the edge of what works in TypeScript (before 4.1 is was significantly past the edge, so that's something, right?). For example, I'd expect weird/bad things to happen with optional or union-typed properties at non-leaf nodes of the object tree. Or objects with string index signatures, for that matter. These might be addressable, but it would take some effort and a lot of testing. The point is: tread carefully.
Playground link to code

Typescript: Enforce a type to be "string literal" and not <string>

Problem
Is there a way in Typescript to define a type that is only a string literal, excluding string itself?
Note that I am not talking about a certain list of string literal; for which, a simple union of "Value1" | "Value2", or an enum type would work. I am talking about any string literal, but not string itself.
Example Code
type OnlyStringLiterals = ...; // <--- what should we put here?
const v1: OnlyStringLiterals = "hi"; // should work
const v2: OnlyStringLiterals = "bye"; // should work
// and so should be for any single string value assigned
// But:
const v3: OnlyStringLiterals = ("red" as string); // should NOT work -- it's string
Use Case
I am doing Branding on the types in my code, and I am passing a brand name, as a template, to my parent class. See the code below:
abstract class MyAbstractClass<
BRAND_T extends string,
VALUE_T = string
> {
constructor(private readonly _value: VALUE_T) { }
getValue(): VALUE_T { return this._value; }
private _Brand?: BRAND_T; // required to error on the last line, as intended!
}
class FirstName extends MyAbstractClass<"FirstName"> {
}
class AdminRole extends MyAbstractClass<"AdminRole"> {
}
class SubClassWithMissedName extends MyAbstractClass<string> {
// I want this to error! ........................ ^^^^^^
}
function printName(name: FirstName) {
console.log(name.getValue());
}
const userFirstName = new FirstName("Alex");
const userRole = new AdminRole("Moderator");
printName(userRole); // Already errors, as expected
Playground Link
I want to make sure every subclass is passing exactly a string literal, and not just string to the parent class.
I found an answer that works for my use case, but is not the most reusable one. Just sharing it anyway.
Thought Process
I believe it's not possible to have one solid type to represent what I wanted, because I cannot even think what will show up in VS Code if I hover over it!
However, to my knowledge, there is a function-style checking in Typescript for types that you can pass a type in and expect a type back, and finally assign a value to it to see if it goes through.
Type-checking using a Generic Type and a follow-up assignment
Using this technique I am thinking about the following template type:
type TrueStringLiterals<T extends string> = string extends T ? never : true;
const v1 = "hi";
const check1: TrueStringLiterals<typeof v1> = true; // No error :-)
const v2 = "bye";
const check2: TrueStringLiterals<typeof v2> = true; // No error :-)
const v3 = ("red" as string);
const check3: TrueStringLiterals<typeof v3> = true; // Errors, as expected!
Playground Link
Easier in an already-passed Generic Type
Also, in my use case, I am doing:
abstract class MyAbstractClass<
BRAND_T extends (string extends BRAND_T ? never : string),
VALUE_T = string
> {
...
Playground Link
... which works like a charm!
You can create utility type which will allow only on subset of string:
type SubString<T> = T extends string ?
string extends T ? never
: T
: never
const makeSubStr = <T extends string>(a: SubString<T>) => a
const a = makeSubStr('strLiteral')
const b = makeSubStr('strLiteral' as string) // error
const c: string = 'elo I am string'
const d = makeSubStr(c) // error
const e: SubString<"red"> = ("red" as string); // error
This type will also return never if something is not a string, in your answer TrueStringLiterals will not take this case into consideration and pass it through.
The other answers don't catch the case where the provided type parameter is a union of literal strings. If this shall be explicitly avoided, as could be read from the OPs question, the following solution, based on the other two can be used:
type UnUnion<T, S> = T extends S ? ([S] extends [T] ? T : never) : never;
type NotUnion<T> = UnUnion<T, T>;
type LiteralString<T extends string> = string extends T ? never : NotUnion<T>;
where UnUnion uses the fact that if T is a union, say 'a' | 'b', the union is distributed over the rest of the type expression.
(['a'|'b'] extends ['a'] ? ... ) | (['a'|'b'] extends ['b'] ? ...)
If T is a union, none of these can hold and all the parts turn into never.
NotUnion reduces this to have just one generic parameter and LiteralString just uses its result in case its parameter is not extendable by string.
Playground Link
I'd like to submit an answer from a similar question I recently asked, that is far more simple than the examples given so far:
type SpecificString<S extends Exclude<string, S>> = S
let test1: SpecificString<"a" | "b" | "c"> // okay
let test2: SpecificString<string> // error
//guaranteed to work where `Exclude<string, T>` wouldn't
let test3: Exclude<SpecificString<"a" | "1">, "1">
test3 = "a" // okay
test3 = "1" // error
Basically how this works:
Exclude<string, "any string literal"> ==> resolves to string
Exclude<string, string> ==> resolves to never
You can call this F-bounded quantification if you like I guess.

How to make compiler check string argument is valid in Typescript?

In TypeScript, how should one go about having the compiler determine whether or not a string is a valid argument to a method/function?
Right now, I am using string literals to accomplish this. For example,
type ValidLetter = "A" | "B" | "C" | "D"; // string literal definition
public PostLetter(letter: ValidLetter) {
...
api.post("https://example.com/letters/", letter);
// POST method only accepts "A", "B", "C", or "D"
}
PostLetter("A") // All good!
PostLetter("Z") // Compiler error
The only thing is, at compile time, I don't know the values I will be passing in to the Post method. I could be receiving any kind of string,
let a = "A";
let foobar = "foobar";
PostLetter(a) // Compiler error
PostLetter(foobar) // Compiler error
What I'm looking for is a way of checking if a string is a valid member of a string literal. I've already attempted using typeof, instanceof, user-defined type guards, and casting. None of them seem to have what it takes.
How would I go about determining that a is a member of ValidLetter and foobar is not? Or perhaps string literals are not the way to go.
TypeScript simply does not do any runtime type checking. Type checking happens at compile time, and type information isn't included in the produced JavaScript file.
post.ts
type ValidLetter = "A" | "B" | "C";
function post(letter: ValidLetter) {
}
Produces the following JavaScript:
post.js
function post(letter) {
}
So you have to re-specify the type check yourself manually in runtime code:
type ValidLetter = "A" | "B" | "C";
function post(letter: ValidLetter) {
if (letter !== "A" && letter !== "B" && letter !== "C") throw "error!";
}
Not too bad. But it's a bit redundant, isn't it?
There's a library called runtypes, that allows you to specify your types once, and it produces a compile-time TypeScript type, as well as keeping type information to do runtime checks:
import { Literal, Union, Static } from 'runtypes'
const ValidLetter = Union(Literal('A'), Literal('B'), Literal('C'));
type ValidLetter = Static<typeof ValidLetter>;
function post(letter: ValidLetter) {
ValidLetter.check(letter);
}
So now you get both full compile-time checks, and runtime checks.
You should be able to do this with a mix of value maps, as well as the user-defined type guards:
const ValidLetterMap = { A: 1, B: 1, C: 1, D: 1 };
type ValidLetter = keyof typeof ValidLetterMap;
declare function postLetter(letter: ValidLetter): void;
postLetter("A"); // ok
postLetter("E"); // err
const a = "A";
postLetter(a); // ok
let a$ = "A";
postLetter(a$); // err, a$ is of type string since it is mutable
function isValidLetter(letter: string): letter is ValidLetter {
return letter in ValidLetterMap;
}
if (isValidLetter(a$)) {
postLetter(a$); // now ok because we've "proven" that a$ is a valid letter
}
Edit: here's a generic form, relying on a slight hack to expose typing.
class StringLiteral<T extends string> {
private literalSet: {[P in T]: true};
// sort of a hack so we can expose a union type of valid letters
public get typeProvider(): T {
throw new Error("typeProvider is only meant for typing info, it has no value");
}
constructor(...literals: T[]) {
this.literalSet = literals.reduce(
(acc, curr) => (acc[curr] = true, acc),
{} as {[P in T]: true}
);
}
public isValid(candidate: string): candidate is T {
return candidate in this.literalSet;
}
}
// how to use
const lettersLiteral = new StringLiteral("A", "B", "C", "D");
declare function postLetter(letter: typeof lettersLiteral.typeProvider): void;
let a$ = "A";
postLetter(a$); // not ok
if (lettersLiteral.isValid(a$)) {
postLetter(a$); // ok!!
}

Swap string case - swift

let str = "tHIS is A test"
let swapped_case = "This IS a TEST"
Swift noob here, how to do the second statement programatically?
This function works with all upper/lowercase characters
defined in Unicode, even those from "foreign" languages such as Ä or ć:
func swapCases(_ str : String) -> String {
var result = ""
for c in str.characters { // Swift 1: for c in str {
let s = String(c)
let lo = s.lowercased() //Swift 1 & 2: s.lowercaseString
let up = s.uppercased() //Swift 1 & 2: s.uppercaseString
result += (s == lo) ? up : lo
}
return result
}
Example:
let str = "tHIS is a test ÄöÜ ĂćŒ Α" // The last character is a capital Greek Alpha
let swapped_case = swapCases(str)
print(swapped_case)
// This IS A TEST äÖü ăĆœ α
Use switch statement in-range checks to determine letter case, and use NSString-bridged methods to convert accordingly.
let str = "tHIS is A test"
let swapped_case = "This IS a TEST"
func swapCase(string: String) -> String {
var swappedCaseString: String = ""
for character in string {
switch character {
case "a"..."z":
let uppercaseCharacter = (String(character) as NSString).uppercaseString
swappedCaseString += uppercaseCharacter
case "A"..."Z":
let lowercaseCharacter = (String(character) as NSString).lowercaseString
swappedCaseString += lowercaseCharacter
default:
swappedCaseString += String(character)
}
}
return swappedCaseString
}
swapCase(str)
I'm a bit too late but this works too :-)
let str = "tHIS is A test"
var res = ""
for c in str {
if contains("ABCDEFGHIJKLMNOPQRSTUVWXYZ", c) {
res += "\(c)".lowercaseString
} else {
res += "\(c)".uppercaseString
}
}
res
In Swift 5 I achieved it by creating a function which iterates through each character of the string, and using string methods to change each character I appended each character back into a new variable:
func reverseCase(string: String) -> String {
var newCase = ""
for char in string {
if char.isLowercase {
newCase.append(char.uppercased())
}
else if char.isUppercase {
newCase.append(char.lowercased())
}
else {
newCase.append(char)
}
}
return newCase
}
Then just pass your string through to the function when you call it in a print statement:
print(reverseCase(string: str))
You already have plenty of good succinct answers but here’s an over-elaborate one for fun.
Really this is a job for map – iterate over a collection (in this case String) and do a thing to each element (here, each Character). Except map takes any collection, but only gives you back an array, which you’d have to then turn into a String again.
But here’s a version of map that, given an extensible collection, gives you back that same kind of extensible collection.
(It does have the limitation of needing both collections to contain the same type, but that’s fine for strings. You could make it return a different type, but then you’d have to tell it which type you wanted i.e. map(s, transform) as String which would be annoying)
func map<C: ExtensibleCollectionType>(source: C, transform: (C.Generator.Element) -> C.Generator.Element) -> C {
var result = C()
for elem in source {
result.append(transform(elem))
}
return result
}
Then to write the transform function, first here’s an extension to character similar to the other answers. It does seem quite unsatisfying that you have to convert to a string just to uppercase a character, is there really no good (international characterset-friendly) way to do this?
extension Character {
var uppercaseCharacter: Character {
let s = String(self).uppercaseString
return s[s.startIndex]
}
var lowercaseCharacter: Character {
let s = String(self).lowercaseString
return s[s.startIndex]
}
}
And the function to flip the case. What I wonder is whether this pattern matching is international-friendly. It seems to be – "A"..."Z" ~= "Ä" returns true.
func flipCase(c: Character) -> Character {
switch c {
case "A"..."Z":
return c.lowercaseCharacter
case "a"..."z":
return c.uppercaseCharacter
default:
return c
}
}
Finally:
let s = map("Hello", flipCase)
// s is a String = "hELLO"
I hope this helps. inputString and resultString are the input and output respectively.
let inputString = "Example"
let outputString = inputString.characters.map { (character) -> Character in
let string = String(character)
let lower = string.lowercased()
let upper = string.uppercased()
return (string == lower) ? Character(upper) : Character(lower)
}
let resultString = String(outputString)

Resources