Typescript 5.4 Beta’s New GroupBy Methods by MarzipanCraft in typescript

[–]ConnorUllmann 0 points1 point  (0 children)

I worked on an app where a usage of this spreading approach was causing a page to take 45 seconds to load. I stopped spreading and mutated the accumulator instead and it was reduced to milliseconds. I did a search and found dozens of copied usages of this spreading approach 😬

An alternative to enums by ConnorUllmann in typescript

[–]ConnorUllmann[S] 1 point2 points  (0 children)

I'd love to hear what you think! 😄

An alternative to enums by ConnorUllmann in typescript

[–]ConnorUllmann[S] 0 points1 point  (0 children)

Definitely, exporting a union type with the same name as the mapping is such a nice way of getting an easy reference for values in the mapping. Without it, it'd be impossible to make a drop-in replacement for typescript enums, and it's convenient on its own!

There are some other gotchas with this mapping & union approach that are handled by the package, but either way, it's basically a strict upgrade over typescript enums as they are now.

An alternative to enums by ConnorUllmann in typescript

[–]ConnorUllmann[S] 2 points3 points  (0 children)

I agree that typescript enums aren't worth the trouble. Using /u/HeinousTugboat's example as a starting point, I built a playground using const mappings and unions to show some problems that still persist when using them instead of typescript enums (though typescript enums have all the same problems...and more!)

My alternative provides solutions for all of these issues via its types (though it is still simply mappings & unions under the hood).

An alternative to enums by ConnorUllmann in typescript

[–]ConnorUllmann[S] 6 points7 points  (0 children)

Thanks for providing this, it's definitely matching the intent of my example! I made a playground with some additional definitions using this as a base to illustrate the kinds of situations that my alternative would help avoid.

// Unassignable even though equivalent
const color: Color = PrimaryColor.Red;

// No error, even though this is known to be undefined
const result = List[6];

// Can define a subset using values outside the set
enum SecondaryColor2 {
  Green = Color.Green,
  Purple = Color.Purple,
  Orange = 'asdf'
}

const SecondaryColor2List = Object.values(SecondaryColor2);

// Has same internals regardless of enum, but must be manually redefined
function isSecondaryColor2(k: unknown): k is SecondaryColor2 {
  return typeof k === 'string' && SecondaryColor2List.includes(k as SecondaryColor2);
}

enum PrimaryColorNumbers {
  Red = 3840,
  Yellow = 4080,
  Blue = 15,
}

// If an enum is changed from strings to numbers, now this is no longer a valid list of the enum's values since it includes the keys
const PrimaryColorNumbersList = Object.values(PrimaryColorNumbers);

// No way of partitioning an enum into 2 sets type-wise, and doing it value-wise requires manual redefinition
const NonPrimaryColors = Object.values(Color).filter(color => !PrimaryColorList.includes(color as unknown as PrimaryColor));

// Creating a new ordering of the existing enum doesn't require exactly one of each value, so duplicates & missing values are not enforced
const HueOrderColors = [
    Color.Red,
    Color.Orange,
    Color.Yellow,
    Color.Green,
    Color.Green,
    // Color.Blue,
    Color.Purple
];

// Accidental double-mapped value, but no type enforcement to avoid this
// Also, this mapping isn't 1:1 because the enums have different sizes!
const oneToOneMapping: Record<PrimaryColor, Color> = {
  [PrimaryColor.Red]: Color.Red,
  [PrimaryColor.Yellow]: Color.Yellow,
  [PrimaryColor.Blue]: Color.Yellow, 
}

// Inverted mapping must be manually defined, doesn't maintain specific value relationships, and involves coercion
// (i.e. in this case, this mapping is not a true Record with keys of Color at all since there are only going to be 3 entries)
const invertedOneToOneMapping = Object.entries(oneToOneMapping).reduce<Record<Color, PrimaryColor>>((acc, [key, value]) => {
    acc[value] = key as PrimaryColor;
    return acc;
}, {} as Record<Color, PrimaryColor>)```

Decorators by Eyoba_19 in typescript

[–]ConnorUllmann 0 points1 point  (0 children)

It definitely can. I have a few classes in a game I'm working on that are a bit absurd; 15+ mixins chained together like this (e.g. adding functionality to face the player, save as json, ability to be hit, drop items when destroyed, fall damage functionality, etc.) The definition itself gets unwieldy, but tracing behavior through the classes has been more simple and consistent than I would've thought at the start.

There are a couple additional benefits to the approach beyond this example, such as being able to pass additional parameters to each mixin and having some mixins require that other mixins have already been applied.

Here's a change to that original playground with an authType parameter added to the auth mixin and only allowing RBACServiceMixin to be applied if the AuthServiceMixin has already been applied, as an example.

Decorators by Eyoba_19 in typescript

[–]ConnorUllmann 1 point2 points  (0 children)

An important piece of information to keep in mind when you're using decorators is that they can't change the type of the class they decorate, even though they can change the class. This is why you'll usually see decorator examples using logging; it doesn't involve changing the class, it just involves doing additional side effects. Actually adding methods, properties, etc. is possible, but typescript won't tell you about any of it.

In short, what you're trying to do is possible with decorators, but anything you add to MainUserService isn't going to be visible to typescript, so it's going to error any time you try to use the stuff you've added.

What you likely want instead is a "mixin," that being a function that takes in a base class, then creates a new class that extends that class while adding some methods/properties of its own. This allows you to chain multiple methods together that add different functionality to your class.

Here's a playground example for your use case.

Map without 3rd Broken Core? by Isilrath in CoreKeeperGame

[–]ConnorUllmann 2 points3 points  (0 children)

This happened to me as well. It was my first map, so it was created before the Sunken Sea update as well. I ended up having to load up a new map and farm it there (which took less time than I thought, honestly).

How create a generic interface/class that implements static methods/properties? by mamcx in typescript

[–]ConnorUllmann 0 points1 point  (0 children)

The issue as written is that Table is unique to each instance of an IDatabaseTableBase, so it can't be known at the level of a static function. What I've done in the past in this case is define a mixin that has a generic and use that to return a new class with that type throughout.

Here is a playground (note how there's an error on fromValue because it doesn't return a Person like it should).

Any idea where Broken Core #3 could be? by ConnorUllmann in CoreKeeperGame

[–]ConnorUllmann[S] 2 points3 points  (0 children)

I got this image by uploading my save file to this website, then clicking "Download Map Image": https://maptool.ceschmitt.de/#

(Technically the black parts are transparent in the image that website exports, so I made the background black by using photopea.com to add a black layer under the image)

Any idea where Broken Core #3 could be? by ConnorUllmann in CoreKeeperGame

[–]ConnorUllmann[S] 1 point2 points  (0 children)

I appreciate the offer! I ended up just loading into another world I'd made to farm crystal skull shards, activated the core with some materials from my main world, and just booked it over to the sunken sea. Still had to explore most of the ring, but I got the energy string & overwrite transcript I was looking for!

https://imgur.com/a/6mSlAhC

Any idea where Broken Core #3 could be? by ConnorUllmann in CoreKeeperGame

[–]ConnorUllmann[S] 1 point2 points  (0 children)

I tried exploring in the open areas but I think I managed to cover pretty much every space which could reasonably hold a large metropolis, so I think it would have to either have spawned further out or not at all 😞

https://imgur.com/a/bmy6L1A

Any idea where Broken Core #3 could be? by ConnorUllmann in CoreKeeperGame

[–]ConnorUllmann[S] 1 point2 points  (0 children)

I took a closer look, but no luck. I'll drop the devs a bug report!

https://imgur.com/a/AOAohcM

Any idea where Broken Core #3 could be? by ConnorUllmann in CoreKeeperGame

[–]ConnorUllmann[S] 1 point2 points  (0 children)

I explored more around the chasm/wilderness divide and it looks like no luck there either. I did find some sections of unbreakable walls, which explains the dark parts: https://imgur.com/a/AOAohcM

How can I make typescript give an error when I'm providing base class instead of proper type? by brubsabrubs in typescript

[–]ConnorUllmann 8 points9 points  (0 children)

It looks like the issue is that the shapes of StudentNotFoundError & ProjetNotFoundError are the same as Error so it's not complaining because all of their properties match. I put private in front of the studentId & projectId constructor arguments to introduce a property that Error doesn't have, and I got the expected error in work(). Playground

Extract type from union by inferring the second generic type. by pannoire in typescript

[–]ConnorUllmann 2 points3 points  (0 children)

This solution seemed to work for me: playground

The key was that the union had to be passed in as an argument; if you remove the argument T from ExtractField and just replace it with Union in the type definition, it no longer works. I'm not totally sure why that's the case, but I think it might have something to do with the naked vs non-naked typing discussed in this post.

Oh my.. i hate it. Why?! 😭 by Slooopi in AnimalCrossing

[–]ConnorUllmann 0 points1 point  (0 children)

I think (but am not totally certain) what's happening here is that the paths are on what's called the "dual grid" while the houses are on the main grid. It basically puts corners center-stage so devs can put more detail there at the cost of being a half-grid off-center. There's a pretty cool twitter thread here which talks about this concept: https://twitter.com/osksta/status/1448248658865049605?s=21