Here's the error
export type mixed = object | number | string | boolean | symbol | undefined | null
export type Is<A> = (m: mixed) => m is A // <= new error
/*
A type predicate's type must be assignable to its parameter's type.
Type 'A' is not assignable to type 'mixed'.
Type 'A' is not assignable to type 'object'.
*/
Related: section "Unconstrained type parameters are no longer assignable to object in strictNullChecks" in https://blogs.msdn.microsoft.com/typescript/2018/05/16/announcing-typescript-2-9-rc/
What's the better fix? Changing mixed to
export type mixed = {} | undefined | null
?
EDIT: best bet so far is
export type mixed = { [key: string]: any } | object | number | string | boolean | symbol | undefined | null
Why don't you add an upper-bound constraint? (As also mentioned in the linked blog post)
export type Is<A extends mixed> = (m: mixed) => m is A
The quest for the perfect definition of mixed still remains, but with a lower priority.
@dippi because that would mean to add an upper-bound constraint everywhere. It would be awkward and, in practice, a breaking change.
I'd rather prefer to find a good definition for mixed. The above candidate seems to work fine based on my experiments but I need some help to test it out in real code bases, especially those containing custom types / combinators.
I see, I'm aware of that issue and I agree with your concerns.
Still I don't know which is the lesser evil, because that definition of mixed leaves a dangerous loophole:
function foo(m: mixed) {
if (m !== null && typeof m === "object" && "bar" in m) {
const bar = m["bar"] // bar has type any
bar.baz // and the compiler won't complain here
}
}
Following my gut I like better the following definition, that needs the upper-bound constraint, but it's less awkward / dangerous to check and access.
export type mixed = { [key: string]: mixed } | number | string | boolean | symbol | undefined | null;
(note: I'm aware of recursive types limitations and the use of interfaces as a workaround, however I just tried it and, to my surprise, iff the type is exported it seems to work fine)
However I honestly cannot fully picture how much awkward it would become and I must admit I don't even use this library, I was just searching for a good definition of mixed and inspiration for run-time type checking when I stumbled upon this issue. So I'll leave the answer to the owners of real code bases.
Unknown type is currently under consideration in typescript
I must admit I don't even use this library
Well, then thank you for taking the time to chime in, more opinions/suggestions are always helpful.
that definition of mixed leaves a dangerous loophole
You are right, my candidate is not a perfect fit for a general purpose definition of mixed.
However looks good enough for io-ts, let me explain what I mean. I chose { [key: string]: any } because it plays well with t.Dictionary. In fact, in "idiomatic" io-ts you would define foo as
function foo(m: t.mixed) {
if (t.Dictionary.is(m) && 'bar' in m) {
const bar = m['bar'] // bar has type mixed
bar.baz // and the compiler DOES complain here
}
}
Finally, as @sledorze pointed out, in the TypeScript repo there's some work related to unknown (https://github.com/Microsoft/TypeScript/pull/24439) and any (https://github.com/Microsoft/TypeScript/pull/24423) so maybe we should just wait the next release and then try to change mixed to
type mixed = unknown
maybe we should just wait...
FYI just tried [email protected] (which already contains unknown) and type mixed = unknown works like a charm
@gcanti what do you think about the proper release / schedule (make it available in 'next' ?)
@sledorze my plan would be
[email protected]) which should contain unknownmixed into an unknown alias (*)[email protected](*) you can try it now editing node_modules/io-ts/lib/index.d.ts
No errors at compilation time.
Looks good to me 馃憤
wait for the next stable release (I guess [email protected])
My guess was wrong, unknown is coming in TypeScript 3, see Announcing TypeScript 2.9.1
@sledorze so in the meanwhile I would go for
export type mixed = { [key: string]: any } | object | number | string | boolean | symbol | undefined | null
WDYT?
@gcanti I need to test it!
@gcanti not compilation error on the code base using your latest proposal.
My project uses io-ts. With that updated definition for mixed I can compile and run it fine with typescript 2.9.1.
@gcanti works also for me with[email protected].
Thanks @sledorze @spacejack @mlegenhausen, I'm going to release a patch
Most helpful comment
Well, then thank you for taking the time to chime in, more opinions/suggestions are always helpful.
You are right, my candidate is not a perfect fit for a general purpose definition of
mixed.However looks good enough for
io-ts, let me explain what I mean. I chose{ [key: string]: any }because it plays well witht.Dictionary. In fact, in "idiomatic"io-tsyou would definefooasFinally, as @sledorze pointed out, in the TypeScript repo there's some work related to
unknown(https://github.com/Microsoft/TypeScript/pull/24439) andany(https://github.com/Microsoft/TypeScript/pull/24423) so maybe we should just wait the next release and then try to changemixedto