#What do I do if a JavaScript library doesn't have type information?

100 messages · Page 1 of 1 (latest)

ornate rivet
#

Sometimes I want to use a library that can be installed via npm i x, but npm i @types/x gives me a 404. On rare occasions, I do find a d.ts file directly inside node_modules/x.

I've followed the TypeScript compiler error messages a couple of times, which led me to define my own d.ts file for that library, but both times--for reasons I can't remember--I could never get my project to compile with that library's d.ts file in my local project.

What are my options here? I'm looking for a nice intersection between type safety for the library API, smallest effort required on my part, and good practice.

cloud spade
#

when using libraries (from npm), 3 options:

  • the library provide its types (available when installing the package)
  • types for that library have been published by the community (available when installing @types/package)
  • you have to write the types yourself, and add them to your sources
#

main idea is that you have a .d.ts file looking something like

declare module "name of the library" {
  // list all the exported classes, types, functions, etc.
  
  // e.g.
  export class Foo {}
  export function bar(baz: boolean): number;
}
#

of course it depends on what the library does
not all libraries work in the same way

#

some of them export functions, while others add items in the global scope, or extend some other packages

#

@ornate rivet

ornate rivet
cloud spade
#

it's written from the perspective of someone who wrote the JavaScript
rigth, but it also applies if you want to write typings for any library in general

ornate rivet
#

Or maybe my issue was I wasn't sure how I'm supposed to modify the tsconfig file to recognize it

cloud spade
#

I'm not sure where I'm supposed to put that file after I write it when it is for another library
it doesn't really matter
the only thing that matters is that it must be included in your sources

ornate rivet
#

ah ok

cloud spade
#

what people sometimes do is put them in a new typings folder at the top level of their project directory

#
src/
  index.ts
typings/
  my-package/
    index.d.ts
tsconfig.json
#

so that TS loads those types

#

(they even mention the typings/ folder)

ornate rivet
uncut crescent
cloud spade
uncut crescent
#

(so no, it's just include)

uncut crescent
ornate rivet
#

what's a "package"?

uncut crescent
#

npm package

cloud spade
ornate rivet
cloud spade
ornate rivet
#

Maybe they mean it won't use a .d.ts file in the current project?

uncut crescent
ornate rivet
#

I'm a little fuzzy, but I think my issue may have been I was using types instead of typeRoots

uncut crescent
#

for your own stuff it's still include/files/exclude...

#

types is the same (also refers to npm packages)

ornate rivet
uncut crescent
#

still not a package

cloud spade
uncut crescent
#

you can declare typings for a library in a regular old .d.ts, that's something typescript lets you do

ornate rivet
uncut crescent
#
declare module './the-import.js' {
}
#

or something like that

#

the './the-import.js' must match however you're importing it in your js

ornate rivet
#

I remember doing something like that. Maybe I used a different keyword like namespace, can't remember

uncut crescent
#

i don't believe declare namespace does the same thing

cloud spade
#

a namespace is just an extra scope
but kinda avoided nowadays

uncut crescent
#

which is quite odd to be fair, considering namespace should be mostly an alias for the (mostly legacy) module...

ornate rivet
#

So are you saying if I wrap the entire d.ts content in declare module './the-import.js' { } all I need to do is include the d.ts file in tsconfig and it can be used for a package?

cloud spade
ornate rivet
#

thonk I tried so many iterations I want to say I tried that. But I'm pretty sure I didn't try 'blah**.js**' ever.

ornate rivet
novel crestBOT
#
n_n#2622

Preview:ts // @filename: foo.d.ts declare module "asdf" { export const t: string } // @filename: aaa.js import {t} from "asdf" const what = t // ^?

uncut crescent
#

!ts

novel crestBOT
#
// @filename: foo.d.ts
declare module 'asdf' {
    export const t: string;
}
// @filename: aaa.js
import { t } from 'asdf';
const what = t;
//    ^? - const what: string```
uncut crescent
#

but i think it only works if the name is exactly the same

cloud spade
#

you usually do the typings for a whole package at a time, not file by file

uncut crescent
ornate rivet
# novel crest

why is the second filename a js extension? Should that be a ts?

uncut crescent
#

oh

#

yeah ts would work too

ornate rivet
novel crestBOT
#

@uncut crescent Here's a shortened URL of your playground link! You can remove the full link from your message.

n_n#2622

Preview:ts // @filename: foo.d.ts declare module "asdf" { export const t: string } // @filename: aaa.ts import {t} from "asdf" const what = t // ^?

cloud spade
uncut crescent
#

!ts

novel crestBOT
#
// @filename: foo.d.ts
declare module 'asdf' {
    export const t: string;
}
// @filename: aaa.ts
import { t } from 'asdf';
const what = t;
//    ^? - const what: string```
uncut crescent
#

but yeah works just the same for ts

uncut crescent
#

if the imports are .js, generally you wouldn't be able to import the package name anyway

#

and same if the imports are 'package-name' and you're trying to declare module 'the-package.js'...

ornate rivet
#

I feel like I could compile the d.ts file OR the rest of the project, but I couldn't figure out how to do both. This probably had to do with me using types/typeRoots without including the defaults or something. Hopefully, there's enough information in this thread to help me next time.

#

I have one more question based off of my hazy memory: I think I've seen a library that puts its d.ts inside its own package. Does that automatically work correctly, or do I have to modify typeRoots to use it?

uncut crescent
uncut crescent
ornate rivet
uncut crescent
#

o_O

split silo
#

(d.ts aren't meant to be compiled)

cloud spade
ornate rivet
uncut crescent
#

nah, what ascor just said

#

typescript looks in the package.json for a types field, which tells it where to find the types for the library

#

if you have local .d.ts files specially for your project, they will be picked up just like your regular .ts files

#

unless it's not covered by the include for some reason

#

e.g. if your code is in src/ but the .d.ts isn't

ornate rivet
#

I could probably Google that one on my own, but my brain never would've assumed that was the way to do it

uncut crescent
#

i'm not sure tsconfig.json even exists on published packages

#

which i assume would be the reason why

uncut crescent
cloud spade
split silo
uncut crescent
split silo
ornate rivet
# split silo (d.ts aren't meant to be compiled)

yeah now there are multiple threads of conversation at once so I made things really confusing. What I mean to say is that either tsc errors complained about the d.ts file, OR they complained about some other part of my project. This was in a scenario where the previous commit, everything compiled fine.

cloud spade
ornate rivet
ornate rivet
#

Anyway, I really complicated this conversation, Sorry. I thought the first thread of discussion was over before I started the second but it wasn't.

#

You have all been very helpful and I think I can mark this as solved.