Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • 08:26
    orta assigned #42899
  • 07:53
    typescript-bot synchronize #42842
  • 07:48
    orta synchronize #42899
  • 07:36
    mattia-lau edited #42982
  • 07:36
    mattia-lau edited #42982
  • 07:34
    mattia-lau opened #42982
  • 07:22
    berickson1 closed #42979
  • 07:02
    rbuckton synchronize #15575
  • 07:01
    rbuckton synchronize #28460
  • 07:01
    rbuckton synchronize #31641
  • 07:01

    rbuckton on master

    Refactor emit substitution into… (compare)

  • 07:01
    rbuckton closed #42676
  • 06:23
    Jack-Works opened #42981
  • 06:09
    LasyIsLazy opened #42980
  • 04:42
    typescript-bot synchronize #42842
  • 03:46
    berickson1 opened #42979
  • 03:44
    tonyload opened #42978
  • 02:45
    mjbvz transferred #42977
  • 02:45
    mjbvz unassigned #42977
  • 02:45
    hi2u opened #42977
webstrand
@webstrand
There's no facility in typescript that prevents you from returning the wrong number, unfortunately.
Bruce Pascoe
@fatcerberus
@wagerfield It’s what we were talking about above - conditional types distribute over unions when given a bare type parameter
Matthew Wagerfield
@wagerfield
@webstrand thanks for the explanation. When I try this, and start typing the second path key in TS, I get autocompletion for "bar", "baz", "a", "b", "c" and "d". eg. export const path: Path<Foo> = ["bar", "" /* <--- here */] whereas I was hoping to get "a" and "b"
webstrand
@webstrand
No idea. autocomplete is just being stupid there.
Keith Layne
@keithlayne
autocomplete might be failing and showing you all the strings it knows
Matthew Wagerfield
@wagerfield
hmmm ok nvm :sweat_smile: —many thanks for your help again @webstrand @keithlayne
Bruce Pascoe
@fatcerberus
The type system is turing complete so it might be that it doesn’t want to evaluate everything just for the sake of intellisense, lest it degrade editing performance
So it takes shortcuts that the compiler doesn’t
Jim Geurts
@jgeurts
@keithlayne last night, you helped me with this. Is there a way to have it work if the protected __entityId = true; gets removed from the abstract Base class, making the base class empty?
it seems that typescript wants some sort of body for a class to determine if a type extends it
webstrand
@webstrand
If that happens IsBase will match all objects
Jim Geurts
@jgeurts
Is there a way to add an identifier to that class object that could be discarded during transpile?
webstrand
@webstrand
you can use declaration merging, just a sec
abstract class Base {
}
interface Base {
  __entityId: true
}
The two declarations get merged and a "virtual" __entityId is added to Base
you can't use protected, though.
alternatively:
abstract class Base {
  protected __entityId!: true
}
the ! tells typescript not to worry if the property isn't initialized
Jim Geurts
@jgeurts
nice! is the declaration merging technique frowned upon or any drawbacks?
webstrand
@webstrand
It's commonly used to augment properties onto modules with incomplete type definitions, so it's not really frowned upon.
There is a bit of type-unsafety, since you are lying to the type checker; there isn't really an __entityId there
It shouldn't be something you commonly find yourself using, though. Most situations have a better solution.
Bruce Pascoe
@fatcerberus
IMO classes should have been nominally typed
webstrand
@webstrand
That said, declaration merging has a wide variety of behaviors, it's also what permits:
type Foo = ...
namespace Foo {
    export function validate(o: unknown): o is Foo
}
Foo.validate(something);
which I use a lot
Bruce Pascoe
@fatcerberus
Structural makes more sense for object literals and interfaces but classes should be nominal
webstrand
@webstrand
and there's nothing unsafe about that
@fatcerberus I think that'd just make the language feel more inconsistent than it already does.
Bruce Pascoe
@fatcerberus
I guess thats true, we’d be explaining the distinction to newbies constantly :stuck_out_tongue:
webstrand
@webstrand
Some way to declare nominal types would be appreciated, though.
John Bartos
@johnBartos
Hey, having some trouble with ReadableStreams when upgrading from TS version 3.x to 4.x
ReadableStreamReadResult seems to have been made private
Is there a detailed migration guide on the lib.dom.ts changes from 3 to 4? I have only seen a warning that a lot of stuff might break
Ralf Vogler
@vogler
can I map a type on elements of a union?
type model = {
  'foo': { a: 1 },
  'bar': { b: 2 },
  'baz': { c: 3 },
}
type variant <m extends keyof model> = model[m] & {model: m};
declare function get <m1 extends keyof model, m2 extends keyof model> (m1: m1, m2: m2) : variant<m1> | variant<m2>;
This let's me discriminate variants by the added field model.
Can I make it variadic without overloading each arity?
declare function geta <m extends keyof model> (...ms: m[]) : variant<m>; // want a union of variants like above instead of a variant of union
Keith Layne
@keithlayne
Ralf Vogler
@vogler
yes! thanks!
there's no way to build a type-level map function since you can't pass type functions as arguments?
type map<f, x> = x extends any ? f<x> : never;
Ralf Vogler
@vogler
Puh, the simple case works, but missing some constraint between the arguments here. It seems to infer the cross-product instead.
Maybe someone gets what I'm trying to do from the code below.
ms and args both influence F and thereby the return type.
type Delegate <M extends ModelName> = prisma.PrismaClient[Uncapitalize<M>];
const unionFindMany = <M extends ModelName, F extends Delegate<M>['findMany']> (...ms: M[]) => async (...args: Parameters<F>) => {
  const uc = (m: ModelName) => m[0].toLowerCase() + m.slice(1) as Uncapitalize<ModelName>;
  const ps = ms.map(uc).map(model =>
    // @ts-ignore This expression is not callable. Each member of the union type '...' has signatures, but none of those signatures are compatible with each other.
    db[model].findMany(args[0])).map(r => ({...r, model}) // no way to introduce a fresh type/existential?
  );
  const rs = await Promise.all(ps);
  return rs.flat() as M extends any ? (Await<ReturnType<F>>[number] & {model: M})[] : never;
}

const xs = await unionFindMany(ModelName.Time, ModelName.TodoMutation)({include: {todo: true}, orderBy: {at: 'desc'}});
if (x.model == ModelName.Time) {
  x // (prisma.Time & { model: "Time" }) | (prisma.TodoMutation & { model: "Time" })
}
Ralf Vogler
@vogler
ok, had to reestablish the constraint on F with the 'element M': as M extends any ? F extends Delegate<M>['findMany'] ? (Await<ReturnType<F>>[number] & {model: M})[] : never : never;
webstrand
@webstrand
@vogler type map<f, x> would require Higher-kinded types, which we don't have right now :frowning:
BTW, M extends M also works, so does M extends unknown
rather than M extends any
Bruce Pascoe
@fatcerberus
Out of curiosity, is T extends T always true? Intuition suggests it should be, but you never know if there's some weird corner case lurking in the dark corners...
Gerrit Birkeland
@Gerrit0
If T is never then T extends T ? 1 : 0 is never
Bruce Pascoe
@fatcerberus
Ah, because of distribution
never is treated as an empty union
But I guess that's true regardless of what's on the RHS of extends
Gerrit Birkeland
@Gerrit0
Yeah, but it surprises a lot of people when they first see it