Have fun!
I'd recommend starting with basic JavaScript before upgrading to TypeScript.
r/learnjavascript+typescript
https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html
I would recommend no namespaces and write code with the ES6 module system.
Until everything updates to ES6 modules, resolution is going to continue being a pain in the ass. Best practice right now is to compile ES6 modules to commonjs modules.
https://www.typescriptlang.org/docs/handbook/modules.html
Edit: I didn't know for a long time that you can still do commonjs imports while still using the ES6 system like this:
import foo = require('bar');
It will never be possible for a translator like this to be truly faithful to the language. Because of TypeScript’s intentional unsoundness, there’s always a possibility that a variable typed as number
is actually a string. Hence a faithful C++ translation would be forced to store it in a tagged union of all possible JS types rather than a simple double
.
You can accomplish this with the new template literal type support in TS. Be warned, though... it'll probably be more annoying than useful.
Here's an example with valid IDs containing a, b, or c and of length 5. Change Char
and ID
to adjust those parameters.
type Char = "a" | "b" | "c"
type ID = ${Char}${Char}${Char}${Char}${Char}
// fails const id1: ID = "abcde" // d and e aren't valid const id2: ID = "ab" // wrong # of letters
// passes const id3: ID = "abaaa" const id4: ID = "ababc"
>How is information hiding implemented in ES6
it's possible, but idiomatically, information hiding isn't really a big deal in js and i don't think it's worth the trouble. having said that, create a closure inside a scope that is inaccessible outside the scope:
const privateFunction = () => { return ' heh'; }; const privateMember = 'heh';
export default class Foo { public publicFunction() { return privateMember + privateFunction(); // closure inaccessible to code using class Foo } }
I answered your first question before I read your second and third question.
Yeah, definitely use TypeScript. It's pretty close to what you're looking for. You may not be be following the exact same route when architecting code, but you'll wind up in nearly the same place. I also used AS3 and also thought it was a pretty good look into what ecma could be, and I think TypeScript is fantastic for communicating contracts and adding stronger enforcement. It will never be quite as powerful for architecting as a statically typed language, but it takes JavaScript and elevates it to about 90% of the way there, which is good enough in my experience.
Can't jerk, it's true. No other languages that I know of has literal types, mapped types, and conditional types. Only TypeScript has the ability to arbitrarily transform type structures with this much power.
(e.g.
type A = {in: ["a"|"b", number], out: string}; type B = {a: 10, b: 20 | 30} type AsyncFn<T> = ... // left as an exercise for the reader type C = AyncFn<A>; // transform any type like A to function type like // C = (in: 10 | 20 | 30, number) => Promise<string>
) Yeah, TS is my favorite language. Fite me
Before you commit to Dart maybe check out TypeScript. You get most of the benefits of Dart but with a much larger community, better tooling, tons of libraries, much better JS interop, etc.
https://www.typescriptlang.org/docs/handbook/type-compatibility.html#a-note-on-soundness
But to give a simple, concrete example:
const map: Record<string, number> = { a: 1 };
const value = map.b; // value is undefined, but type system says it's a number
TypeScript cannot generate the functions for you, but you can write them yourself and TypeScript can narrow types for you based on control flow. Better explanation here: https://www.typescriptlang.org/docs/handbook/advanced-types.html
any
is way more dangerous: you can directly assign string
to any
and any
to number
without a peep from the compiler. It’s an explicit escape hatch from the type system, intended for interfacing with untyped JavaScript. If we’re talking about “fully typed” TypeScript, it’s reasonable to exclude any
.
{}
is the empty interface, which is a supertype of both string
and number
, since they both vacuously have all the members of {}
. (The new <code>unknown</code> type would have worked just as well in my example.) You can directly assign string
to {}
but not {}
to number
. By itself, {}
would be a fine part of a sound type system. (If {}
seems confusing or suspicious, you can substitute an arbitrary base class and two of its subclasses for {}
, string
, and number
, e.g. Shape
, Triangle
, and Circle
.)
The problem is that, although converting number
to {}
is safe, converting number[]
to {}[]
is not, because arrays aren’t read-only. (ReadonlyArray<{}>
would be fine.) But the TypeScript developers judged that prohibiting this unsafe assignment would be too inconvenient, so TypeScript doesn’t complain.
This is just one of several decisions that the TypeScript developers intentionally made to value convenience over soundness. You can agree or disagree with these decisions (personally, I’m a fan of “disagree”), but one of the consequences is that you can’t write a faithful translator that compiles TypeScript number
to native double
.
I find the official Typescript docs to be a very good resource, they nailed it with their documentation
Here's the section from the docs.
Their suggestion is:
> If you would like a heuristic, use interface
until you need to use features from type
.
> One thing I’m struggling with, though: I have a class with a few attributes that get their values in different phases.
Use a builder pattern: playground
You mention HTMLDivElement
, but JSX expressions translate to React.createElement
calls, which return data structures that haven't been "materialized" into a true DOM element yet. However, it is true that this type information is unretained, regardless of the VDOM process: this is corroborated by both the TypeScript handbook on JSX as well as React's own typings.
What is your use case for needing to know what kind of runtime representation a JSX expression can mount? Is it related to refs?
You have strictNullChecks disabled (this is TS default). I recommend setting the project to strict mode by adding strict: true
to your tsconfig
Typescript does this. Example. The return type is inferred to be a union type based on the return statements.
I suggest you use TypeScript instead of Babel, it transpiles to plain javascript (ES5/ES3) with generators and async/await, TS will be more suitable for you, because you have CPP background which is a strongly typed language like TypeScript: https://www.typescriptlang.org/docs/handbook/basic-types.html
> How are the transpilers making this code work on older browsers
they basically use state machines.
> There's a new flavor coming; Typescript. Not sure if it will gain momentum or not
Coming? Not sure if it will gain momentum? Typescript is already extremely popular.
> If it does and eventually it gets a JIT and advanced language features, e.g. generics, then it might become The One. I think it will take a couple more iterations though
...
JIT is antithetical to Typescript's approach: https://softwareengineering.stackexchange.com/a/275499
Generics: https://www.typescriptlang.org/docs/handbook/2/generics.html
It's really odd for you to make sweeping statements when you don't seem to know anything about Typescript other than it's name and that it compiles to JS.
This feels like pedantry, but... This isn't an example of TypeScript 'making types real' or TypeScript doing run-time type-checking. The type guards are just Javascript, and all the runtime type-checking that is being done here is being done by Javascript. However, TypeScript understands (or can be made to understand) these type guards, and can thus use them for type inference in the following code.
Which is nice, but you don't need to use TypeScript to do the runtime type checking that is being done in this article.
tl;dr: with readonly properties, class properties will more and more be used, not just for internal state but also to expose that state to the outside. If we'd be using getters to do this, we'd make interfaces for this behaviour, so it makes sense to now also allow properties on interfaces.
There are already a few languages that do this:
> Does this not make you feel unstable given all the dramas about react-router?
That's a great question, and the answer is no. We have existing apps using our old framework (which relied on an earlier version of React Router). We were able to implement codemods to migrate to the RR4 API that the Fusion.js plugin exposes. While the code to make this happen isn't the prettiest, we think we can provide automatic migrations again should the need arise.
> Also, you use Flow over Typescript, when Flow is increasingly having an unknown future, while Typescript is on the rise.
We chose Flow for a number of reasons. One of the main ones is type inference. For example: https://www.typescriptlang.org/play/#src=function%20a(x)%20%7B%0D%0A%20%20return%20x%20-%201%3B%0D%0A%7D%0D%0A%0D%0Aa('a') https://flow.org/try/#0GYVwdgxgLglg9mABAQwBQA8CUiDeAoRRAJwFMoQil1EBaRARgG48BfPPNAcmU8yA
With Flow, we can surface type errors even in files that don't have any type information other than the @flow directive, e.g. http://eng.uber.com/wp-content/uploads/2018/07/image4.png
Another major reason is again related to migrations. It's not feasible to get hundreds of apps to migrate to TS. The type inference capabilities of Flow make it less daunting to gradually increase type coverage, both in framework space as well as app space as apps buy into static typing.
I do wish Flow had the typing ecosystem that typescript has, and I even joked with my coworkers that the first thing people would ask when we release Fusion.js was going to be Typescript bindings :)
>Do you need to change your target library? Try changing the lib compiler option to 'es2015' or later.
The error is telling you you need to update your lib property in tsconfig. Older versions of JS don't have the fill method, and TS doesn't know what env you're trying to support so it won't let you do it
More information in the official docs: https://www.typescriptlang.org/tsconfig#lib
TypeScript has a sophisticated system of <em>type guards</em> and <em>narrowing</em> that allow it to type-check common dynamically-typed patterns in JS code.
An example from that documentation page:
function padLeft(padding: number | string, input: string) { if (typeof padding === "number") { // padding has type number here return new Array(padding + 1).join(" ") + input; } // padding has type string here return padding + input; }
Notice that the type of padding is not just narrowed within the if body, but also in the subsequent code (because the if body always returns).
To write the equivalent in Rust you would need to be more explicit about introducing enums and pattern-matching, which has its relative merits and demerits.
Specifically, it's a "definite assignment assertion". More info: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html
It's only necessary if you use --strictPropertyInitialization. Otherwise it's a no-op.
You've probably already dome it, but start here and read through the TS handbook: https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html
Given you're experienced in JS I'd just say aim to recreate a small to mid sized project you've previously done to gain an appreciation of the benefits of TS over JS.
TS isn't dramatically different, but it is notably different to JS!
I had to look up what the “is” operator does since I had not used it before. It was slightly annoying to google because “is” is a common word, so here’s the documentation for anyone else.
https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards
It's not "!:", it's just "!", the other stuff is part of whatever else you're doing there. This is the "non-null assertion operator": https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator
You're telling Typescript "I promise this value will never be null, so don't nag me about not checking it before referencing".
Let's first see what type Type<T = {}> = T & { Type: string }
means in context of usage:
type A = { a: string }; type B = { b: string };
type Type1 = Type; type A1 = Type<A>; type B1 = Type<B>;
(TypeScript Playground link attached here)
Now, hovering on A1
will show you A & { Type: string }
. The actual type will be the intersection of A
and { Type: string }
-- so it basically means type A1 = { a: string, Type: string }
, and respectively, type B1 = { b: string, Type: string }
.
So what does this syntax mean?
type Type<T> = ...
means type Type
is <em>generic</em> over T. We can provide a default generic type on absence of explicit T--and this is exactly what happens when writing type Type<T = {}> = ...
- we say that if we don't provide T
, we should use {}
instead.
We can see it in action if we hover over Type1
in our example.
Yup, TypeScript is its own thing which isn't tied to any framework/library. You can use it with regular JS, other frameworks like React, etc. You can even use it on the server-side with Node.
If you haven't already, install node/npm here: https://nodejs.org/en/
Then, install TypeScript globally by running this in your terminal:
$ npm install -g typescript
Then, just navigate to your project folder and run:
$ tsc --init
This will automatically create a tsconfig.json
file for you which will allow you to start using TypeScript.
If you want to customize how TypeScript works with your project, you can change settings in the tsconfig.json
file. You can see specifics on what you can do here: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html
Basically the way you should think about TypeScript is that it's ES2015 and beyond, with optional static types.
If you're familiar with JSX, it's conceptually similar to how JSX adds some syntax on top of JavaScript. The difference is that types in TypeScript don't have any emit - they're erased away.
The benefits of the type system are not just catching errors and typos, but that we can build a lot of great tooling around it.
You can try it out on https://www.typescriptlang.org/play
This would actually be better represented with something without IMPLIED meaning. In typescript it would be possible to represent it with a union or intersection type.
https://www.typescriptlang.org/docs/handbook/advanced-types.html
In this way you could actually have multiple "there is no available credit score" values, not just the single one implied by NULL.
This was my first time hearing about this event. Really cool!
Here's what I came up with:
type EventsType = {
[BookType in Books as on${BookType['genre']}
]?: (e: BookType) => void
}
Link to playground
The biggest problem with TS is that the type system is kind of quirky. It's mostly due to the fact that it assumes that no side effects ever happen. Which is fair, but it can lead to some uncomfortable situations (warning: contrived example), or situations where you've worked around some limitation in the type system via `any`, `as`, or `!`, and then are bitten in the ass later because some "invariant" you were upholding turns out to not be so invariant...Still better than debugging type errors at runtime.
Completely depends on the project, and how strict they felt like being.
Unless all the contributors spent the up-front effort to avoid any
and eliminate warnings and errors (and fail the build as soon as any are reintroduced), I don't get the fearless refactoring I do with Elm.
The old module
has been renamed namespace
.
A note about terminology: It’s important to note that in TypeScript 1.5, the nomenclature has changed. “Internal modules” are now “namespaces”. “External modules” are now simply “modules”, as to align with ECMAScript 2015’s terminology, (namely that module X { is equivalent to the now-preferred namespace X {).
https://www.typescriptlang.org/docs/handbook/namespaces-and-modules.html
This system is still supported, but designed for client-side code without module system or bundler.
Most bigger JS apps now is either Node.js or uses a bundler -- webpack, babel, browserify, requirejs, maybe also ES6 async loader in the near future. The new module system can compile down to support those:
I would follow ES6 module system if your bundler is compatible enough. ``` import * as jquery from 'jquery'; import MyClass from './MyClass'; import namedExport1, namedExport2 from './MyUtil';
export default MyClass; export { namedExport1, namedExport2 }; ```
typescript occasionally generates runtime code for you (eg if your target doesn't support async) but yeah it's pretty rare. In general the fact that typescript doesn't exist at runtime is probably why people get confused if they're used to other languages
> For example, there's no way to define an interface an implementation has to conform to, it's not clear from function declarations what types their parameters are supposed to be
Maybe try TypeScript?
> it's not possible to dynamically load code without bad hacks like eval
eval
isn't really a hack, it's just the JS compiler exposed at run time as a function. Not sure why it would kill optimization since the compiler shouldn't care whether a function was created from a source file or an eval
call, and any dynamic code loading will be a security nightmare, period. Definitely doesn't make the stack traces any easier to read though, and forget about breakpoints.
EDIT: Just for fun, I made a little benchmark to test a function made from eval
vs. a regular function. There is no speed difference in my browser.
You can extend multiple interfaces and have those properties on the new one.
Example from handbook:
interface Shape { color: string; }
interface PenStroke { penWidth: number; }
interface Square extends Shape, PenStroke { sideLength: number; }
let square = <Square>{}; square.color = "blue"; square.sideLength = 10; square.penWidth = 5.0;
I liked this explanation but I wanted something I could play with, so I've made some examples in this playground.
type TypedAtCallsite = <T>(t: T) => T
type TypedAtDefinition<T> = (t: T) => T
type Counter = {count: number}
const logAndReturn: TypedAtCallsite = (thingToLog) => {
console.log(thingToLog)
return thingToLog
}
const addToCounter: TypedAtDefinition<Counter> = (counter) => ({
count: counter.count + 1
})
logAndReturn<Counter>({count: 5})
addToCounter({count: 5})
Using discriminated unions seems to work
Echoing what other people have mentioned, TS is compiled down to JS at the end of the day. Although it's not essential starting out, eventually you'll need to know it if you want to do it professionally.
You can compare what the resulting JS is using https://www.typescriptlang.org/play/
There is a typescript Discord where you can ask specific questions. There is also the typescript documentation and playground (good for messing with snippets).
https://www.typescriptlang.org/community
https://www.typescriptlang.org/tools
More generally, I've ported previous hobby projects to new tech stacks in my spare time to learn them.
You're trying to using interface declaration merging to change the definition of Task
. But declaration merging cannot change the type of an interface property - it can only add properties. From the handbook:
> Non-function members of the interfaces should be unique. If they are not unique, they must be of the same type.
In this case you're trying to change the property type from string
to a subtype of string
, and yes "choice"
is assignable to string
- but it's still a different type.
You just need to use generics on the class in the same way you'd use generics on a function.
Hope this is not a stupid question.
I have an array with some elements that could be null
.
I filter out those values but TS doesn't know and still thinks that a value could be null
.
What's the best approach here? I could simply slap a promotions?.discount
in there and the error would go away, but maybe there's a cleaner way to tell TS "at this point the array has been filtered so it's guaranteed that there are no more null
values".
Thanks in advance.
The only way you can do this is to wrap both parameters into a single object:
interface UnknownEntity { type: string; }
interface UnknownEntityPair { x: UnknownEntity; y: UnknownEntity; }
interface EntityA { type: "a"; }
interface EntityAPair { x: EntityA; y: EntityA; }
const bothAreEntityA = (pair: UnknownEntityPair): pair is EntityAPair => { const {x, y} = pair; return x.type === "a" && y.type === "a"; }
Here's an example.
TypeScript is a superset of JavaScript developed by Microsoft. "Superset" means that everything that is written in TypeScript can be converted to plain vanilla JavaScript. The main benefit of TS is written in its name: It's a typed language. That means that you can define types for variables like in C/C++.
For example, a string declaration in vanilla JavaScript would be
let myVar = "Hello World";
In TS, you would write it like
let myVar : string = "Hello World";
If you then try to assign e.g. a number to myVar, your TS compiler would give you an error at compile time.
here is more to read.
Thats the non-null assertion operator. Basically tells Typescript to remove possible null or undefined values from a type. Generally useful when trying to access a nested property or method on something that may be null in some cases, but you know won't be in a given context.
> I was all for typescript until I tried to find the type definition file for node 8 so I could use async/await.
Definitions were added two weeks ago.
> Also had issues when I tried to use sqlite3 in my project. Typescript threw pages and pages of errors into the terminal on build for any ts file that dared to reference sqlite 3.
> Also, how am I supposed to import npm packages? node syntax, es6 syntax?
Node syntax is allowed, but you should use ES6 syntax, similar to Babel.
Write some JavaScript and save it to a file.
Now, take that file and change the extension from .js to .ts. Congratulations - you've written your first TypeScript program.
TypeScript is a superset of JavaScript - you can get started as easy as that, and just incrementally add TypeScript's new features as you get comfortable with them.
See for yourself: Start with 'Classic JavaScript' and work your way down:
Well that depends a lot on your thoughts on best practice. It could not hurt to check if a function that adds two numbers together was actually passed numbers. But at the same time this adds a great amount of overhead to your project.
So is worth it? Probably not for simple functions but may be worth it when writing a tool set.
If you would like some pre built data types and type checking then I would recommend checking out Typescript it does this for you.
To echo this and provide a little more to the OP:
Because your parameter type was (c: string) => void
you are correct that it is a required parameter (non-optional). But that requirement only exists when calling the function, not defining a function to conform to the type.
I've taken your original typescript playground and extended it to show the difference:
Quote from typescriptlang.org:
> The default visibility of class members is public. A public member can be accessed anywhere.
> Because public is already the default visibility modifier, you don’t ever need to write it on a class member, but might choose to do so for style/readability reasons.
Yup. Can confirm yourself on TS playground here.
Maybe! Here's something close but it's not quite there and feels over-complicated. We might think about this one a bit.
TypeScript is explicitly unsound: https://www.typescriptlang.org/docs/handbook/type-compatibility.html#a-note-on-soundness. So a language just having soundness as a goal pretty inherently means it will be more sound than TS, absent major implementation fault.
You can use the <code>@typedef</code> and <code>@property</code> JSDoc tags to create a pseudo-interface declaration inside a regular .js
file. You can't create an actual interface, though. Check out the link for more JSDoc tricks for TypeScript.
Alternatively, put your interfaces in a separate d.ts
file.
So the reason this happens is because TS only knows that the items in there are Drawable
s, and not that they are Hero
es. You basically have two ways of fixing this:
a) Remove the explicit type declaration for characters
(or define all keys explicitely) so TS infers the type automatically as { heroArray: Hero[], zombieArray: Zombie[]
. Then TS knows that this object has only the keys heroArray
and zombieArray
and the types of those, respectively.
b) If you want to keep it as a collection of Drawable
s, the quick and dirty way is suggested by /u/blood__drunk below; Just cast it and tell TS "I know better than you". The more beautiful way is writing a so called "User Defined Type guard" Docs:
const possibleHero = characters.heroArray[0]; if (isHero(possibleHero)) { possibleHero.whatever(); }
function isHero(drawable: Drawable): drawable is Hero { return typeof (<Hero>drawable).whatever === 'function'; }
This allows you to programmatically decide if the variable is a hero, a zombie or whatever and then call these specific methods on it.
While I don't think an outright merge would ever happen because big egos, the next best thing is for the projects to steal each others' great ideas until the projects are essentially the same.
For Flow, stealing Typescript's ideas hasn't gotten a lot of traction, unfortunately. For example, the Github issue to enable Flow to import the established TypeScript .d.ts has sat there since 2014 without an implementation (https://github.com/facebook/flow/issues/7).
But thankfully the TypeScript folks have had a lot more success at poaching the good stuff from the Flow ecosystem. For example, as of at version 2.4, TypeScript can infer types (so for example let x = 3 will set x to be a number). (https://www.typescriptlang.org/docs/handbook/type-inference.html). As I remember it, this was the one huge advantage that Flow had over TS when it first came out.
The TypeScript compiler will omit import
statements that are solely used for types. In your case, it was omitting the game.js
and main-menu-state.js
imports because you never used Game
or MainMenuState
as values; only as type annotations. Type annotations aren't there in the compiled runtime code, so the import statement doesn't need to be there either.
Fortunately, you don't need to type-check the objects -- or change any other logic -- to fix the problem. You simply need to force your modules to import for their side-effects.
import { Game } from './engine/game.js'; import { MainMenuState } from './menu/main-menu-state.js'; import './engine/game.js'; import './menu/main-menu-state.js';
Alternatively, refer to the imports in a value position so that the import statements aren't stripped.
Game, MainMenuState; // no-op statement
Explained here:
https://www.typescriptlang.org/docs/handbook/modules.html#import-a-module-for-side-effects-only
While I appreciate the time taken to describe higher level abstractions, I really hope nobody uses the advice in this article.
Implementing Maybe/Optional in JS is an exercise in futility: It already has the ability to indicate that a value is present or absent (undefined
or null
).
Just
/Nothing
constructs are needless overhead and can cause additional problems, as there's nothing preventing references you expect to be Just
or Nothing
to be simply undefined
or null
.Also, these dependencies are not free. Building the following code with Webpack 4 (for production, so it's minified and tree-shaken) produces a bundle that is 18,670 bytes (copying the article's recommendation to use require()
and not import
syntax, you don't get tree shaking so the production bundle is 64,753 bytes):
import Maybe from 'crocks/Maybe'; import safe from 'crocks/Maybe/safe'; import prop from 'crocks/Maybe/prop'; import { compose, isNil, not } from 'ramda'; console.log(Maybe, safe, prop, compose, isNil, not);
I suggest adding a disclaimer to the article indicating that this is for educational purposes and should not be used.
This is a very primitive example, but it describe the issue:
interface Data { data: string; value: string; }
function getData(): Data { const result = { data: 'data', value: complexSlowMethod() }; return result; }
Assume that complexSlowMethod()
is a method that aggregates the data for value
. The type of result
is inferred to be { data: string; value: string }
, it is not inferred to be Data
. Now when returning result
the TypeScript compiler will check if the type of result
is compatible with the return type, which is the case. Sounds okay, so far. But if you later decide to remove the value
property from Data
, then the code will continue to run just as is. If you're explicit with the type of the variable result
by writing const result: Data = {..}
, then instead the TypeScript compiler will complain: Object literal may only specify known properties
, so the TypeScript compiler supports you and tells you about an unknown property, about (potentially slow) code that can be removed because it's not needed anymore. If you infer the type, compatibility is just checked, and it's compatible. By being explicit TypeScript will support you in eliminating unwanted properties.
The code works fine, but if you annotate the type explicitly TypeScript will warn you about the value
property that is not part of the interface.
Have a look at TypeScript. From their website:
> TypeScript is a superset of JavaScript that compiles to JavaScript.
Best part is that it has pretty good tooling around it and many big companies have used it. One of the best examples I could think of is the ImmutableJS library developed by Facebook, which uses TypeScript.
This is my solution! The UnionIntersection type pretty much is what I needed, thanks a bunch!
Hi, here you can see an example where the "outDir" option is used to do precisely that. https://www.typescriptlang.org/tsconfig#outDir
I think you should absolutely use it to have your transpiled files in a separate directory. This will make your life easier when scrolling through your files, using git (with .gitignore), deleting+rebuilding all transpiled code, etc.
It will be easier if you think of tsc as a tool to compile TypeScript to JavaScript, and nothing else. If you have multiple TS files, the output will be multiple JS files.
Once tsc compiles everything to JS, you're essentially left with a regular JS project. Whether you run that in node or on the web is up to you. Whether you concatenate all of the files into one file is up to you.
AMD, System, ES* are module systems that exist outside of the TS ecosystem. You likely use one or many of them in your existing JS projects (or maybe not, which is fine!) and you should pick one based on how your project will be run. If in doubt, check the docs.
If they are differnet, then that isn't really an interface that you can extend. You could create a single type that uses template literal types to interpolate a string into the key from a generic.
While cool that the compiler supports this, I find it way too annoying to use in practice (unless there is a need for super safe, like formally verified levels safe, code, but then maybe TS might not be the best choice anyways). To ensure a normal array is non-empty and make it be accepted by the type, one can't just use basic condition checking, custom type guards must be used instead all over the place (or I guess you could just cast it, but that to me kind of defeats the effort in the first place).
Similarly when you have a non-empty array that is actually accepted by the type (whether it was directly instantiated as such, type guard checked, and or manually cast), you can fool the type checking even with basic operations like .pop(), which don't change the type and now you have a false sense of security.
I think a better solution if one really wants a bit more security at the cost of more bloated and annoying-to-write code, is using the checked indexed accesses compiler option. And extensive high coverage tests of course.
I just tested a toy example in the TS Playground, and it looks like it works to me.
const x1: /applications/${string}/commands
= '/applications/test/commands';
const f = (param: /${string}
) => param;
const x2: /${string}
= f(x1);
The error is thrown from your resulting JS code. Compileroptions > module is where your problem is I think. Your target is ES5 but you're specifying esnext modules. Set it to commonjs and you should be good to go.
Here's a playground with this approach. The only issue I found was that it only enforces that all keys and values are present, not necessarily that the key and value are always the same. They can be swapped and the type will still be valid.
What you're most likely looking for is how to define your own declaration file:
https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html
Here is documentation specifically for npm modules:
https://www.typescriptlang.org/docs/handbook/declaration-files/templates/module-d-ts.html
Here's what looks to be a gentler intro into declaration files:
My recommendation is to gradually define the types for the package on a need-to basis. I did this for Stripe's JS SDK a few years ago when they didn't yet have official type definitions. So initially my declaration file looked like this:
declare module 'stripe' { export interface Customer { // etc .. }
export function getCustomer(id: string) => Promise<Customer> }
Something like this?
interface IApplication { id: number; createdAt: string; applicationStatus: "INTERVIEWING" | "RESUMESUBMITTED" | "OFFERRECIEVED"; url: string; }
declare function count(applications: IApplication[]): Record<IApplication['applicationStatus'], number>
// ...which is eqivalent to declare function count2(applications: IApplication[]): { [K in IApplication['applicationStatus']]: number }
const numInterviewing = count([]).INTERVIEWING // inferred as number
And given that op is new to typescript :
(A | B | C) extends undefined ? true : false
by definition is :
(A extends undefined ? true : false) | (B extends undefined ? true : false) | (C extends undefined ? true : false) |
And if one of A
,B
,C
is undefined the one of the previous for lines is true which leads to the result :
true | false | false
which actually is :
true | false
i.e
boolean
But here is how I would do it. You should treat extends in types like some kind of equality.
This is a good question, I was also confused for a bit after reading it.
The thing is, the way you defined hashMap
, you told typescript that every key of hashMap always is a { valueNumber: number; valueString: string; }
. This might not be intuitive, but when you are doing hashMap[stringKey] || "string";
, TS believes that hashMap[stringKey]
is always defined, and therefore the value after the ||
is not even considered in the type resolution.
If you want these ||
to act as you expect, you have to tell TS that hashMap[something]
might not be defined, like so:
const hashMap: { [key: string]: { valueNumber: number; valueString: string; } | undefined; } = {};
You absolutely can create functions that will determine if a value conforms to a certain type/interface. They're called type guards. Here is some documentation.
So type guards are cool, however you have to rely on correctly implementing the function, as well as updating it should your type/interface change. This is time consuming and error prone.
There are libraries to help with this. One I recently stumbled upon which I like is runtypes. It's great because you can define your type/interface and the guard at the same time. You can then use it as a type/interface, and it also provides helper methods to check that a given unknown value conforms.
import \* as rt from 'runtypes';
export const Request = rt.Record({
username: rt.String,
password: rt.String,
});
export type Request = rt.Static<typeof Request>;
const myValue = {};
if (!Request.guard(myValue)) {
// myValue does not conform to Request
}
I believe the favored model is to export each function and allow the use of external modules instead of exporting an internal one. That's assuming you're working with modules and not the global scope.
See the "Do not use namespaces in modules" section at the bottom of the page.
https://www.typescriptlang.org/docs/handbook/modules.html
A snippet: > When first moving to a module-based organization, a common tendency is to wrap exports in an additional layer of namespaces. Modules have their own scope, and only exported declarations are visible from outside the module. With this in mind, namespace provide very little, if any, value when working with modules.
Keep in mind that naming collisions are handled by the importer using the 'as' keyword.
If you first filter out the keys that you don't want, then your resulting type won't have those nasty : never
properties either. And the infer U
is pointless if you don't use U
, so you can just use any
.
type OutputCallbackKeys<T> = { [K in keyof T]: T[K] extends EventEmitter<any> ? K : never }[keyof T]; type OutputCallbacks<T> = { [K in OutputCallbackKeys<T>]: T[K] extends EventEmitter<any> ? () => void : never; };
OutputCallbackKeys
will be a union type of string literal types that map to all properties that extend EventEmitter<any>
.
Ping /u/tme321
I think you’re wrong about that first part.
{ [clientId: string]: MyType }
defines a dictionary/hash object with dynamic string keys of MyType
values. Not the return type of a function call.
I agree with the second part, but here’s a bit more detail:
IConnection & { client}
is an intersection type that should throw a compilation error in most cases because the type of client
is not explicitly declared, it will be considered any
if it compiles at all.
We have the handbook on our website, and there's Basarat Ali Syed's TypeScript Deep Dive, both which are free. There are a bunch of other resources though (books, online courses, etc) that I haven't looked through yet.
We also have a Vue quick-starter that I need to update for 2.5 now that we don't need an experimental git branch :D
Do you know about TypeScript?
> “TypeScript helped us to reuse the team’s knowledge and to keep the same team velocity by providing the same excellent developer experience as C# ... A huge improvement over plain JavaScript.” > > — Valio Stoychev, PM Lead - NativeScript
I haven't checked into the Express ecosystem in months after switching to Koa!
And now that async-await is moving into Node, the release of the technically-dev 2.x branch should be coming up soon (next few months). I would not recommend Koa over Express for a new project if you're unable to transpile your code from ES2017 or TypeScript (my preferred solution) AND you cannot use --harmony (async-await is available behind the harmony flag in v7.3.0).
If you need a more enterprisey solution or need it in production before ~April and don't want to rely on a technical preview, I've heard good things about Hapi.
tl;dr — Koa for days!
Looks like maybe you have a different tsconfig in your environment vs in the playground. For example if I enable useDefineForClassFields
, then I get different behavior. I think the expected behavior here is not strictly defined and I wouldn't rely on it acting a certain way.
Funny you mention TS -- when some scripts I wrote at work became too complex, I actually rewrote them in Node using the Typescript-in-JSDoc syntax.
The function 'getIndex()' takes a Line or Rectangle type. This means that 'geometry' is either a Line or a Rectangle. The classes Rectangle and Line method getIndex only accept one type.
Use an interface to solve it: (untested)
interface Geometry { getIndex(node: number, geometry: Geometry): number; } function getIndex(geometry: Geometry) { geometry.getIndex(1, geometry); // Argument of type 'Line | Rectangle' is not assignable to parameter of type 'Line & Rectangle'. // Type 'Line' is not assignable to type 'Line & Rectangle'. // Property 'width' is missing in type 'Line' but required in type 'Rectangle'.ts(2345) }
class Line implements Geometry { x1: number; constructor() { this.x1 = 10; } getIndex(node:number, geometry: Geometry) { //some math here ... return 0; } }
class Rectangle implements Geometry { width: number; constructor() { this.width = 10; } getIndex(node:number, geometry: Geometry) { //some math here ... return 0; } }
Playground:
Hey everyone! Author here
I'm super proud of the type system we were able to build around react navigation and think it can help a lot of people out if you're working in a large react-navigation codebase. It also makes use of some very cool recent language features such as Template Literal Types and Variadic Tuple Types.
Assuming you know JS because you're asking on this sub, start here: Typescript for JS Programmers
Typescript was deliberately designed so that it could be introduced gradually and incrementally into a Javascript codebase. If you want a detailed view of that, check out the engineering blog from Bloomberg. It's not always easy, and not always painless to migrate, that article has a detailed and well-balanced take.
Then to try it next, take an existing project you have and try to convert a single file to TS. Review the TS config options (controls how strict the type checks are) or Google "gradually migrate to Typescript". See if you can eventually get that one TS file to pass the type checks on the strictest setting.
I tried this a couple years ago on a personal project, and was surprised (but glad) to see that the type checker caught a couple of places where I was calling a method on a variable that might be null. Haven't gone back to JS since.
Types can only be applied to TypeScript files, and .jsx
files are JavaScript files. You'll need to change the extension to .tsx
to use types, and also ensure your environment and/or build process can turn TypeScript into JavaScript.
Have you tried the triple slash directive: https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html
For the extensions I have made in Azure DevOps I have to include at the top of my files:
/// <reference types="vss-web-extension-sdk">
That way I can access the globally scoped variables that the DevOps library exposes on its own, but still get the type hinting/safety of the library.
Try doing that with a reference to your gtag library.
Anyhow, if you want me to do your nonsense task, here you go.
It has nothing to do with multiplication whatsoever, but hey. Whatever floats your boat.
I hope this does what you want. It became quite easy to do with the recent addition of template literal types.
As the earlier poster /u/iams3b said, there's nothing wrong with the code, but if you target an earlier ES version, you do get an error on TS Playground.
Perhaps follow the advice in the error message and target ES2015 or later..
Look at the Utility Types for inspiration https://www.typescriptlang.org/docs/handbook/utility-types.html
In your example, B could be defined in terms of A as
type BAttrs = Pick<A, 'a'>; interface B extends Omit<A, keyof BAttrs> { attributes: BAttrs }
You can manually set the type of this
in a function by specifying a this
parameter:
required: function(this: YourType) { ... }
The TypeScript Handbook has a section on "Declaring this in a Function" if you want to know more.