6 ms·
Unconventional Ways to Cast in TypeScript
- Octoth0rpe 1y agoSigh, the abuse of void was particularly eye-opening for me. If one really must do this - and I can think of a couple of cases where one might mostly around progressively porting old codebases to typescript - I'd strongly prefer the simple `a as unknown as B;` as one can easily grep for ` as unknown as ` to find your crimes.
- fenomas 1y agoFunny, just yesterday I found myself casting in a way I'd never seen before: const arr = ['foo'] as ['foo'] This wound up being useful in a situation that boiled down to: type SomeObj = { foo: string, bar: string } export const someFn = (props: (keyof SomeObj)[]) => {} // elsewhere const props = ['foo'] as ['foo'] someFn(props) In a case like that `as const` doesn't work, since the function doesn't expect a readonly argument. Of course there are several other ways to do it, but in my case the call site didn't currently import the SomeObj type, so casting "X as X" seemed like the simplest fix.
- c-hendricks 1y agoI generally put lint rules to prevent casting, why cast here instead of declaring `props: (keyof SomeObj)[]` or `props: Parameters<typeof someFn>[0]`?
- fenomas 1y agoEr, my justification was that the code in question was meant to be minimally demonstrating someFn, and adding an import or a verbose type seemed to distract from that a little. But mostly it just gave me a chuckle. I tried it because it seemed logical, but I didn't really think it was going to work until it did..
- pverheggen 1y agoWhy not use annotation instead? const props: ['foo'] = ['foo']
- halflife 1y agoOr: cons foo = [‘foo’] as const;
- wk_end 1y ago> In a case like that `as const` doesn't work, since the function doesn't expect a readonly argument.
- halflife 1y agoAh, missed that. Sorry.
- cat-whisperer 1y agoI don’t get this? why do I need to say as const?
- fenomas 1y agoDidn't occur to me, that's certainly more defensible! Though maybe less humorous.
- NathanaelRea 1y agoYou could also do const arr = ["foo" as const]
- Latty 1y agoThat's also different: "foo"[] as opposed to ["foo"] or readonly ["foo"].
- deleted 1y ago[deleted]
- huflungdung 1y ago[dead]
- metadaemon 1y agoSurprised the `satisfies` operator wasn't called out
- callumgare 1y agoHow would that work? My understanding is that satisfies doesn’t change anythings type, it just provides an additional type validation check.
- Latty 1y agoI believe satisfies will narrow the type const infers. It won't lose any information, so you can check it satisfies a broader type without stopping it being used as a narrower one, but it will narrow down the inference if given (but you can of course widen it back out with an explicit typing).
- seniorsassycat 1y agoThat doesn't cast or change compile time type right?
- worik 1y agoThis why I find Typescript frustrating. It really should be called "vaguely typed script"
- culi 1y agoit's gradually and structurally typed and I think that's what makes it great. I also disagree that it's vague. Nowadays you can even have typesafe regex
- shepherdjerred 1y agoTypeScript is incredibly safe if you use strict tsc settings and ts-eslint strict presets. If it were this strict out-of-the-box, it probably would have been hated since most devs don't really want to deal with static typing. https://typescript-eslint.io/getting-started/typed-linting https://typescript-eslint.io/getting-started/typed-linting
- worik 1y ago> TypeScript is incredibly safe I expect this comment is from a person who has not used strongly typed languages extensively. Is that true? When "1" == 1, and "1" can cross a wire and be assigned to a number, when there are no ints... Perhaps I have not given Typescript enough rope, I only spent eighteen months immersed It is not shit, but it is very outdated IMO. I have been known to be wrong, I admit, but all these "unconventional " casts, together with the standard ones, are exactly what frustrated me
- 1y ago
- culi 1y agoThis post is trying to solve a problem you should never have to solve. The function in the post: const cast = <A, B,>(a: A): B => a as unknown as B; should never be used in a professional codebase. The post even admits (though, in my opinion, understates) as much: > If you're holding it right, these things don't come up, and your code genuinely is much much safer than if you used raw Javascript. Just wanted to highlight this point I feel needs to be underscored
- mpeg 1y agoEh it's ok to use `as unknown as X` sometimes If you have complex types, it's sometimes the easiest way to do what you want, and it's perfectly safe as long as you are 100% sure that the types are compatible. For example, where you have a fluent-style API where each method modifies the types it's unavoidable to end up using that kind of cast
- aniviacat 1y ago> it's perfectly safe as long as you are 100% sure That was funny to read
- mpeg 1y agoIf you disagree, you're welcome to prove me wrong! To give you an example from a popular open source ts-heavy project: https://github.com/elysiajs/elysia/blob/94abb3c95e53e2a77078bcdd652cd55877b6a697/src/index.ts#L5786-L5826 https://github.com/elysiajs/elysia/blob/94abb3c95e53e2a77078... The `return this as any` there, which effectively casts it to the same type this had, but with the added get route is perfectly safe, it works, and will never be a problem by itself.
- lovich 1y agoIt’s funny because the reason you used a language like typescript is because you want the compiler to be 100% sure that it’s compatible, not relying on human reasoning. If you were going to rely on that anyway, why not just use JavaScript as is and avoid the boilerplate from typescript
- beezlewax 1y agoCan't you change settings in ts to make it more 'strict' than this ?
- nawgz 1y agoI think the title undersold it. I would call it "a nonexhaustive set of abuses of casting in TypeScript" I actually thought that calling out the `is` operator was useful, I have a generic utility method I call `filterFalsy` which takes `T | undefined | null` and returns `arg is T`, which seems decently safe, but it's interesting how it would fail when asserting between two types. Unconvention 2 made me vomit, inlining a function like that would never pass code review, indeed if you remove the mutator from being declared inline it restores type soundness as you would expect Unconvention 3 is important to learn, TS "duck typing" is not resilient to usage of spread/Object.(keys|values|entries) Unconvention 4 is why I argue to use `callback: () => unknown` whenever you won't use the return type, it is the least restrictive and also implies to stay the hell away from that return value. Fun article.
- kerneloops 1y agoAnother one: const cast: <A, B> (a: A) => B = function <B> (): B { return arguments[0] } const n: string = cast(1)