A very well put response Luke, couldn't agree more... I find JS (plus JSDoc) easier for projects that I'll mainly work on alone or in a small team and TypeScript beneficial when many people will work on the project.
Strangely enough it was Vite which significantly lowered the adoption barrier for me (I don't use VS Code).
esbuild's transform is so fast and the "To hell with the TypeScript errors, lets run this code now!" approach means that I don't constantly get stalled having to explain (right now, this very moment) to TypeScript what I'm doing. It's only once I get far enough into the weeds that I switch gears and go into "type linting" mode to shore up the work already done.
I do think there are two significant milestones in TypeScript competence:
type consumer
type producer
It's fairly easy to get to the "type consumer" level and that's typically all that's required when duct-taping dependencies together to "make a thing".
Maintainers of dependencies however need to aspire to the "type producer" level and that is quite a bit more work because TypeScript has a lot of concepts that other statically typed languages simply don't need (they aren't trying to bridge the gap to a dynamically typed language where it isn't unusual (and sometimes even desirable) for types to morph at run time).
The less you rely on dependencies and the more you are crafting bespoke capabilities, the more you need to push into "type producer" territory.
The one thing I still don't like is the pressure TypeScript places on coding style. Just recently I ended up this the tight ball of code:
asyncfunctionrootAndRun<T>(timeoutMs:number,factory:Factory<T>):Promise<T>{letdisposeFn:(()=>void)|undefined;lettimeoutId;try{returnawaitnewPromise((resolve,reject)=>{createRoot((dispose)=>{disposeFn=dispose;timeoutId=setTimeout(functiontimeout(){timeoutId=undefined;reject(newError('Timed out'));},timeoutMs);// queueMicrotask/setTimeout allows `setup` to finish// before exercising the reactive graph with `run`construn=factory(functiondone(data,err){if(data===undefined)reject(err);elseresolve(data);});if(typeofrun==='function')queueMicrotask(run);});});}finally{if(disposeFn){disposeFn();disposeFn=undefined;}if(timeoutId){clearTimeout(timeoutId);timeoutId=undefined;}}}
The JavaScript version was neatly pulled apart into separate functions
asyncfunctionrootAndRun(timeoutMs,factory){letdisposeFn;lettimeoutId;try{returnawaitnewPromise(executor);}finally{if(disposeFn){disposeFn();disposeFn=undefined;}if(timeoutId){clearTimeout(timeoutId);timeoutId=undefined;}}// ---functionexecutor(resolve,reject){createRoot((dispose)=>{disposeFn=dispose;timeoutId=setTimeout(timeout,timeoutMs);// queueMicrotask/setTimeout allows `setup` to finish// before exercising the reactive graph with `run`construn=factory(done);if(typeofrun==='function')queueMicrotask(run);});// ---functiontimeout(){timeoutId=undefined;reject(newError('Timed out'));}functiondone(data,err){if(err!=undefined)reject(err);elseresolve(data);}}}
When I started typing it, TypeScript was constantly complaining how the same T could end up being different types. It's only once I inlined the functions that TypeScript finally "got it".
So when people claim that TypeScript makes refactoring easier they simply mean that it (likely) rings the alarm bells when something doesn't quite line up after you moved some code around.
However I claim that TypeScript doesn't encourage refactoring—in the sense of authoring code that is broken down into easily digestible chunks.
TypeScript's type inference pushes you toward an "inlined" coding style (which I absolutely despise) because inlined code is easier on the type inference. If you want to break things down into sensibly named and sized chunks it penalizes you with a hefty explicit typing tax which most people aren't willing to pay—leading to heavily inlined code which the JavaScript ecosystem already has enough of.
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
A very well put response Luke, couldn't agree more... I find JS (plus JSDoc) easier for projects that I'll mainly work on alone or in a small team and TypeScript beneficial when many people will work on the project.
Strangely enough it was Vite which significantly lowered the adoption barrier for me (I don't use VS Code).
esbuild's transform is so fast and the "To hell with the TypeScript errors, lets run this code now!" approach means that I don't constantly get stalled having to explain (right now, this very moment) to TypeScript what I'm doing. It's only once I get far enough into the weeds that I switch gears and go into "type linting" mode to shore up the work already done.
I do think there are two significant milestones in TypeScript competence:
It's fairly easy to get to the "type consumer" level and that's typically all that's required when duct-taping dependencies together to "make a thing".
Maintainers of dependencies however need to aspire to the "type producer" level and that is quite a bit more work because TypeScript has a lot of concepts that other statically typed languages simply don't need (they aren't trying to bridge the gap to a dynamically typed language where it isn't unusual (and sometimes even desirable) for types to morph at run time).
The less you rely on dependencies and the more you are crafting bespoke capabilities, the more you need to push into "type producer" territory.
The one thing I still don't like is the pressure TypeScript places on coding style. Just recently I ended up this the tight ball of code:
The JavaScript version was neatly pulled apart into separate functions
When I started typing it, TypeScript was constantly complaining how the same
T
could end up being different types. It's only once I inlined the functions that TypeScript finally "got it".So when people claim that TypeScript makes refactoring easier they simply mean that it (likely) rings the alarm bells when something doesn't quite line up after you moved some code around.
However I claim that TypeScript doesn't encourage refactoring—in the sense of authoring code that is broken down into easily digestible chunks.
TypeScript's type inference pushes you toward an "inlined" coding style (which I absolutely despise) because inlined code is easier on the type inference. If you want to break things down into sensibly named and sized chunks it penalizes you with a hefty explicit typing tax which most people aren't willing to pay—leading to heavily inlined code which the JavaScript ecosystem already has enough of.