Using enums in Typescript's generics with strictFunctionTypes - function

I have the following code (TS playground link):
const enum Enum { A, B, C }
interface Args {
e: Enum.A;
}
interface GenericClass<A> {
new (args: A) : void;
}
class TestClass {
constructor(args: Args) {}
}
function func<A>(C: GenericClass<A>, args: A) {
return new C(args);
}
func(TestClass, { e: Enum.A });
The last line [1] throws an error with strictFunctionTypes enabled:
Argument of type 'typeof TestClass' is not assignable to parameter of type 'GenericClass<{ e: Enum; }>'.
Types of parameters 'args' and 'args' are incompatible.
Type '{ e: Enum; }' is not assignable to type 'Args'.
Types of property 'e' are incompatible.
Type 'Enum' is not assignable to type 'Enum.A'.
That's strange because I accept exact enum value Enum.A and I pass the exactly same value Enum.A into function.
I know I can use type casting { e: <Enum.A>Enum.A }, but it looks strange for me. Is here a way to fix this problem without type casting?

I am not 100% sure why this happens, but I belive that when inferring A the compiler will consider both places where A appears and decide that the widest possible type is { e:Enum} based on the fact that object literals don't usually infer literal types for their fields. After the inference it will see that under strict functions the type is not compatible with the class. Under this theory, if we decrees the priority of the second inference site, we should get the correct type for A,. We can do this using an intersection type A&{} (I am not sure where exactly I read this but it was in a github issue and a member of the compiler team mentioned that this way of decreasing inference priority is probably going to work for the foreseeable future) .
Again that is mostly an educated guess, but the solution works :
const enum Enum { A, B, C }
interface Args {
e: Enum.A;
}
interface GenericClass<A> {
new (args: A) : void;
}
class TestClass {
constructor(args: Args) {}
}
function func<A>(C: GenericClass<A>, args: A & {}) {
return new C(args);
}
func(TestClass, { e: Enum.A });
playground link

Related

How to infer a generic type in kotlin

I've done this code and don't uderstand what could be wrong :
fun <T : ComplicationDataSourceService> getCorrespondingComplicationService(): Class<T>? {
return when (this) {
IMMOBILITY -> ImmobilityComplicationService::class.java
HEART_RATE -> null
POWER_BUTTON -> null
SHORTCUT -> null
}
}
class ImmobilityComplicationService: ComplicationDataSourceService() {
...
}
I got this compilation error :
Type mismatch, required:
Class<T>?
Found:
Class<ImmobilityComplicationService>
Thanks
Your function signature claims that the caller can choose a T, and it'll return a Class<T> for that T, no matter what it is.
That's not true. Your function returns a Class for some subclass of ComplicationDataSourceService, but the caller doesn't pick that type.
Instead, the correct type of your function is
fun getCorrespondingComplicationService(): Class<out ComplicationDataSourceService>?

How do I load JSON as objects with Enums using TypeScript? [duplicate]

Is there any way to have a TypeScript enum compatible with strings from JSON?
For example:
enum Type { NEW, OLD }
interface Thing { type: Type }
let thing:Thing = JSON.parse('{"type": "NEW"}');
alert(thing.type == Type.NEW); // false
I would like thing.type == Type.NEW to be true. Or more specifically, I wish I could specify the enum values to be defined as strings, not numbers.
I am aware that I can use thing.type.toString() == Type[Type.NEW] but this is cumbersome and seems to make the enum type annotation confusing and misleading, which defeats its purpose. The JSON is technically not supplying a valid enum value, so I shouldn't type the property to the enum.
So what I am currently doing instead is using a string type with static constants:
const Type = { NEW: "NEW", OLD: "OLD" }
interface Thing { type: string }
let thing:Thing = JSON.parse('{"type": "NEW"}');
alert(thing.type == Type.NEW); // true
This gets me the usage I want, but the type annotation string is way too broad and error prone.
I'm a bit surprised that a superset of JavaScript doesn't have string based enums. Am I missing something? Is there a different way this can be done?
Update TS 1.8
Using string literal types is another alternative (thanks #basaret), but to get the desired enum-like usage (above) it requires defining your values twice: once in a string literal type, and once as a value (constant or namespace):
type Type = "NEW" | "OLD";
const Type = {
NEW: "NEW" as Type,
OLD: "OLD" as Type
}
interface Thing { type: Type }
let thing:Thing = JSON.parse(`{"type": "NEW"}`);
alert(thing.type === Type.NEW); // true
This works but takes a lot of boilerplate, enough that I don't use it most of the time. For now I'm hoping the proposal for string enums will eventually make the roadmap.
Update TS 2.1
The new keyof type lookup allows for the string literal type to be generated from the keys of a const or namespace, which makes the definition a little less redundant:
namespace Type {
export const OLD = "OLD";
export const NEW = "NEW";
}
type Type = keyof typeof Type;
interface Thing { type: Type }
const thing: Thing = JSON.parse('{"type": "NEW"}');
thing.type == Type.NEW // true
Update TS 2.4
TypeScript 2.4 added support for string enums! The above example becomes:
enum Type {
OLD = "OLD",
NEW = "NEW"
}
interface Thing { type: Type }
const thing: Thing = JSON.parse('{"type": "NEW"}');
alert(thing.type == Type.NEW) // true
This looks nearly perfect, but there's still some heartache:
You still have to write the value twice, ie OLD = "OLD", and there's no validation that you don't have a typo, like NEW = "MEW"... this has already bitten me in real code.
There's some oddities (perhaps bugs?) with how the enum is type checked, its not just a string literal type shorthand, which is what would be truly correct. Some issues I've bumped into:
enum Color { RED = "RED", BLUE = "BLUE", GREEN = "GREEN" }
type ColorMap = { [P in Color]: number; }
declare const color: Color;
declare const map: ColorMap;
map[color] // Error: Element implicitly has an 'any' type because type 'ColorMap' has no index signature.
const red: Color = "RED"; // Type '"RED"' is not assignable to type 'Color'.
const blue: Color = "BLUE" as "RED" | "BLUE" | "GREEN"; // Error: Type '"RED" | "BLUE" | "GREEN"' is not assignable to type 'Color'.
The equivalent code with enum Color replaced by string literal types work fine...
Yeah, I think I have OCD about this, I just want my perfect JS enums. :)
If you are using Typescript before the 2.4 release, there is a way to achieve that with enums by casting the values of your enum to any.
An example of your first implementation
enum Type {
NEW = <any>"NEW",
OLD = <any>"OLD",
}
interface Thing { type: Type }
let thing:Thing = JSON.parse('{"type": "NEW"}');
alert(thing.type == Type.NEW); // true
Typescript 2.4 has built in support for string enums already, so the cast to any would be no longer necessary and you could achieve it without the use of String Literal Union Type, which is ok for validation and autocomplete, but not so good for readability and refactoring, depending on the usage scenario.
In case someone's still looking at this question in 2021:
#Aaron wrote in the original question:
This looks nearly perfect, but there's still some heartache:
You still have to [...]
enum Color { RED = "RED", BLUE = "BLUE", GREEN = "GREEN" }
type ColorMap = { [P in Color]: number; }
declare const color: Color;
declare const map: ColorMap;
map[color] // Error: Element implicitly has an 'any' type because type 'ColorMap' has no index signature.
// [...]
The equivalent code with enum Color replaced by string literal types work fine...
Yeah, I think I have OCD about this, I just want my perfect JS enums. :)
1. keyof typeof enumObj
Regarding,
The equivalent code with enum Color replaced by string literal types work fine...
use the typeof and keyof operators in chained conjunction.
type ColorKeys = keyof typeof Color
type ColorMap = { [P in ColorKeys]: number; } // will have strongly typed keys
No more implicit any when accessing map: ColorMap.
This will work with numeric enums as well (which can (and should more often than not) be const).
From Typescript Handbook - Enums at compile time:
Even though Enums are real objects that exist at runtime, the keyof keyword works differently than you might expect for typical objects. Instead, use keyof typeof to get a Type that represents all Enum keys as strings.
2. ts-enum-util
Check out ts-enum-util, which offers strongly typed interfaces to (likely) all your enum-related needs.
TS 2.9.2
My solution:
export enum Enums { VALUE1, VALUE2 }
and when I have value from API json:
switch (response.enumValue.toString()) { //can be without toString if we have string value from JSON.
case Enums[Enums.VALUE1]:
...
case Enums[Enums.VALUE2]:
...
}
but the type annotation string is way too broad and error prone.
Agreed. One quick workaround (if you have the luxury of code generation you can automate this):
interface Thing { type: "NEW" | "OLD" }
These are called string literals in a union. More : https://basarat.gitbooks.io/typescript/content/docs/tips/stringEnums.html
I've been using converter functions as a stopgap. Hopefully this thread comes to a resolution: https://github.com/Microsoft/TypeScript/issues/1206
enum ErrorCode {
Foo,
Bar
}
interface Error {
code: ErrorCode;
message?: string;
}
function convertToError(obj: any): Error {
let typed: Error = obj as Error;
// Fix any enums
typed.code = ErrorCode[typed.code.toString()];
return typed;
}

How does the Liskov Substitution Principle apply to function return types?

The Liskov Substitution Principle states that:
Objects in a program should be replaceable with instances of their sub-types without altering the correctness of that program.
Assuming that:
interface Iterable<T> {
fun getIterator(): Iterator<T>
}
interface Collection<T> : Iterable<T> {
val size: Int
}
interface List<T> : Collection<T> {
fun get(index: Int): T
}
interface MutableList<T> : List<T> {
fun set(index: Int, item: T): Unit
}
When LSP is applied to input parameters, the lowest-level abstraction should be applied:
DO
fun foo(items: Iterable<Any>) { ... }
DON'T
fun foo(items: List<Any>) { ... }
But, does LSP apply to function return types, and if so, does the reverse apply?
fun bar(): Iterable<Any> { ... }
OR
fun bar(): List<Any> { ... }
Yes and yes. In order to comply with the LSP, argument types in an overriding method must be contravariant, as you point out. The reverse is true for the return type -- this must be covariant, i.e. of the same type, or a more specific type, as the return type in the method being overidden.
Think of the slogan "demand no more, promise no less." Let's say the superclass method returns a Rectangle. This method can be overidden to return a Square, as this "promises more," but not to return a Shape, as this would "promise less."

Kotlin: catch extension

Because Kotlin doesn't support multiple catch like java does, I want to create extension to partially solve the problem.
fun <T: Throwable> (() -> Unit).catch(vararg exceptions: KClass<T>, catchBlock: (Throwable) -> Unit) {
try {
this()
} catch (e: Throwable) {
if (e::class in exceptions) catchBlock(e) else throw e
}
}
That can be called like this:
{
throw NotImplementedException.exception()
}.catch(NotImplementedException::class) {
//handle it
}
But the problem is that if to pass several arguments with different types it doesn't work (Type inference failed):
{
throw IndexOutOfBoundsException()
}.catch(NotImplementedException::class, IndexOutOfBoundsException::class) {
}
So how can I change signature of the extension to catch several exceptions of different types?
Let's look at the types of the two arugments you're trying to pass to your function:
val kclass1: KClass<NotImplementedException> = NotImplementedException::class
val kclass2: KClass<IndexOutOfBoundsException> = IndexOutOfBoundsException::class
While they are both KClass instances, their type parameters are different - NotImplementedException and IndexOutOfBoundsException. This means that no generic T type parameter can be found for the function that would fit both of these types exactly.
Just for demonstration and explanation purposes, you could help type inference by casting both of your types to KClass<Throwable> (or KClass<Exception>, or KClass<RuntimeException, you get the idea) yourself, that way it could figure out the generic type:
{
throw IndexOutOfBoundsException()
}.catch(NotImplementedException::class as KClass<Throwable>, IndexOutOfBoundsException::class as KClass<Throwable>) {
println("Caught something: $it")
}
But the real solution is to use the out keyword to specify use-site variance for the type parameter of the KClass instances:
fun <T : Throwable> (() -> Unit).catch(vararg exceptions: KClass<out T>, catchBlock: (Throwable) -> Unit) {
try {
this()
} catch (e: Throwable) {
if (e::class in exceptions) catchBlock(e) else throw e
}
}
This way the compiler will find a type for T that's both a subtype of Throwable as specified, and is a supertype of all argument's KClass type parameters - this will be RuntimeException in this case, which you can find out by opening intention actions on the catch call (Alt + Enter on Windows, ⌥↩ on macOS) and choosing Add explicit type arguments. This will produce the following:
{
throw IndexOutOfBoundsException()
}.catch<RuntimeException>(NotImplementedException::class, IndexOutOfBoundsException::class) {
println("Caught something: $it")
}

Typescript `enum` from JSON string

Is there any way to have a TypeScript enum compatible with strings from JSON?
For example:
enum Type { NEW, OLD }
interface Thing { type: Type }
let thing:Thing = JSON.parse('{"type": "NEW"}');
alert(thing.type == Type.NEW); // false
I would like thing.type == Type.NEW to be true. Or more specifically, I wish I could specify the enum values to be defined as strings, not numbers.
I am aware that I can use thing.type.toString() == Type[Type.NEW] but this is cumbersome and seems to make the enum type annotation confusing and misleading, which defeats its purpose. The JSON is technically not supplying a valid enum value, so I shouldn't type the property to the enum.
So what I am currently doing instead is using a string type with static constants:
const Type = { NEW: "NEW", OLD: "OLD" }
interface Thing { type: string }
let thing:Thing = JSON.parse('{"type": "NEW"}');
alert(thing.type == Type.NEW); // true
This gets me the usage I want, but the type annotation string is way too broad and error prone.
I'm a bit surprised that a superset of JavaScript doesn't have string based enums. Am I missing something? Is there a different way this can be done?
Update TS 1.8
Using string literal types is another alternative (thanks #basaret), but to get the desired enum-like usage (above) it requires defining your values twice: once in a string literal type, and once as a value (constant or namespace):
type Type = "NEW" | "OLD";
const Type = {
NEW: "NEW" as Type,
OLD: "OLD" as Type
}
interface Thing { type: Type }
let thing:Thing = JSON.parse(`{"type": "NEW"}`);
alert(thing.type === Type.NEW); // true
This works but takes a lot of boilerplate, enough that I don't use it most of the time. For now I'm hoping the proposal for string enums will eventually make the roadmap.
Update TS 2.1
The new keyof type lookup allows for the string literal type to be generated from the keys of a const or namespace, which makes the definition a little less redundant:
namespace Type {
export const OLD = "OLD";
export const NEW = "NEW";
}
type Type = keyof typeof Type;
interface Thing { type: Type }
const thing: Thing = JSON.parse('{"type": "NEW"}');
thing.type == Type.NEW // true
Update TS 2.4
TypeScript 2.4 added support for string enums! The above example becomes:
enum Type {
OLD = "OLD",
NEW = "NEW"
}
interface Thing { type: Type }
const thing: Thing = JSON.parse('{"type": "NEW"}');
alert(thing.type == Type.NEW) // true
This looks nearly perfect, but there's still some heartache:
You still have to write the value twice, ie OLD = "OLD", and there's no validation that you don't have a typo, like NEW = "MEW"... this has already bitten me in real code.
There's some oddities (perhaps bugs?) with how the enum is type checked, its not just a string literal type shorthand, which is what would be truly correct. Some issues I've bumped into:
enum Color { RED = "RED", BLUE = "BLUE", GREEN = "GREEN" }
type ColorMap = { [P in Color]: number; }
declare const color: Color;
declare const map: ColorMap;
map[color] // Error: Element implicitly has an 'any' type because type 'ColorMap' has no index signature.
const red: Color = "RED"; // Type '"RED"' is not assignable to type 'Color'.
const blue: Color = "BLUE" as "RED" | "BLUE" | "GREEN"; // Error: Type '"RED" | "BLUE" | "GREEN"' is not assignable to type 'Color'.
The equivalent code with enum Color replaced by string literal types work fine...
Yeah, I think I have OCD about this, I just want my perfect JS enums. :)
If you are using Typescript before the 2.4 release, there is a way to achieve that with enums by casting the values of your enum to any.
An example of your first implementation
enum Type {
NEW = <any>"NEW",
OLD = <any>"OLD",
}
interface Thing { type: Type }
let thing:Thing = JSON.parse('{"type": "NEW"}');
alert(thing.type == Type.NEW); // true
Typescript 2.4 has built in support for string enums already, so the cast to any would be no longer necessary and you could achieve it without the use of String Literal Union Type, which is ok for validation and autocomplete, but not so good for readability and refactoring, depending on the usage scenario.
In case someone's still looking at this question in 2021:
#Aaron wrote in the original question:
This looks nearly perfect, but there's still some heartache:
You still have to [...]
enum Color { RED = "RED", BLUE = "BLUE", GREEN = "GREEN" }
type ColorMap = { [P in Color]: number; }
declare const color: Color;
declare const map: ColorMap;
map[color] // Error: Element implicitly has an 'any' type because type 'ColorMap' has no index signature.
// [...]
The equivalent code with enum Color replaced by string literal types work fine...
Yeah, I think I have OCD about this, I just want my perfect JS enums. :)
1. keyof typeof enumObj
Regarding,
The equivalent code with enum Color replaced by string literal types work fine...
use the typeof and keyof operators in chained conjunction.
type ColorKeys = keyof typeof Color
type ColorMap = { [P in ColorKeys]: number; } // will have strongly typed keys
No more implicit any when accessing map: ColorMap.
This will work with numeric enums as well (which can (and should more often than not) be const).
From Typescript Handbook - Enums at compile time:
Even though Enums are real objects that exist at runtime, the keyof keyword works differently than you might expect for typical objects. Instead, use keyof typeof to get a Type that represents all Enum keys as strings.
2. ts-enum-util
Check out ts-enum-util, which offers strongly typed interfaces to (likely) all your enum-related needs.
TS 2.9.2
My solution:
export enum Enums { VALUE1, VALUE2 }
and when I have value from API json:
switch (response.enumValue.toString()) { //can be without toString if we have string value from JSON.
case Enums[Enums.VALUE1]:
...
case Enums[Enums.VALUE2]:
...
}
but the type annotation string is way too broad and error prone.
Agreed. One quick workaround (if you have the luxury of code generation you can automate this):
interface Thing { type: "NEW" | "OLD" }
These are called string literals in a union. More : https://basarat.gitbooks.io/typescript/content/docs/tips/stringEnums.html
I've been using converter functions as a stopgap. Hopefully this thread comes to a resolution: https://github.com/Microsoft/TypeScript/issues/1206
enum ErrorCode {
Foo,
Bar
}
interface Error {
code: ErrorCode;
message?: string;
}
function convertToError(obj: any): Error {
let typed: Error = obj as Error;
// Fix any enums
typed.code = ErrorCode[typed.code.toString()];
return typed;
}