I'm using typescript with angular. Can't import this type definition because it has no export.
I had to add this to index.d.ts:
export = SegmentAnalytics;
export as namespace SegmentAnalytics;
It's similar to what is in type definitions for other packages such as c3.
Is this correct - shall I create a pull request?
@types/segment-analytics package and had problems.Definitions by: in index.d.ts) so they can respond.These types were originally drafted under the assumption that Segment has been loaded with the web snippet (i.e. https://segment.com/docs/sources/website/analytics.js/quickstart/) and that there's a global analytics object attached to the window by Segment. Assuming that's the case, you can load global types in a module system by adding something like this.
/// <reference path="./path/to/node_modules/@types/segment-analytics/index.d.ts" />
There's a analytics.js module in NPM, but last I checked, Segment wasn't encouraging its users to import the module directly for use with Webpack, Browserify, etc. (see https://github.com/segmentio/analytics.js/issues/398#issuecomment-217277574).
Also, does the export = ... edit actually work for you? This type definition is saved under @types/segment-analytics but there's no actual segment-analytics NPM module. That is, I could see TypeScript compiling import { identify } from "segment-analytics by adding an export = ... to the definition, but that shouldn't work during runtime because "segment-analytics" isn't a real package name. Assuming this is a use case we want to support, we probably should create a @types/analytics.js package.
Yes I'm loading segment through the web snippet like they recommended. I'm not using the module in npm.
The export method worked for me as I wasn't using the global 'analytics' variable you declare, I was really just using your AnalyticsJS interface and grabbing the actual analytics variable from window, i.e.:
import { AnalyticsJS } from 'segment-analytics';
import { Inject } from '@angular/core';
interface IWindow {
analytics: AnalyticsJS;
}
export class MyClass {
constructor (@Inject(Window) private window: IWindow) {
if (window.analytics) {
window.analytics.identify('[email protected]');
}
}
That being said, another approach is I could do the following:
declare const analytics;
export class MyClass {
constructor() {
analytics.identify('[email protected]');
}
}
But I don't think that uses your types file.
I could not get your /// <reference ... approach to work. Kept getting 'Cannot find name 'analytics'' error.
OK, I understand what you're doing now. Yes, the export = ... works if you directly import the definition from the package.
However, adding an export changes the definition from a global file to a module, which means that the declare var analytics: SegmentAnalytics.AnalyticsJS line would no longer tell TypeScript that there's a global analytics variable. The intended usage was to simply have analytics and the SegmentAnalytics namespace be globally accessible without any imports.
I'm not sure neither the global or /// <reference ... are being picked up for you though. What does your tsconfig.json look like? Or what commands are you passing to tsc?
Here's a simple example that builds without having to rely on /// or importing in definitions: https://github.com/fongandrew/tsc-segment-test
I ran your simple example and it builds fine for me. I made an equivalent simple angular project - https://github.com/hisham/angular-segment-test - and added code similar to yours and I get the following errors.
ERROR in /Users/hisham/src/angular-segment-test/src/main.ts (12,29): Cannot find namespace 'SegmentAnalytics'.
ERROR in /Users/hisham/src/angular-segment-test/src/app/app.component.ts (12,5): Cannot find name 'analytics'.
ERROR in /Users/hisham/src/angular-segment-test/src/app/app.component.ts (13,5): Cannot find name 'analytics'.
tsconfig.json is as follows:
{
"compileOnSave": false,
"compilerOptions": {
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es5",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2016",
"dom"
]
}
}
It looks like the problem is here: https://github.com/hisham/angular-segment-test/blob/master/src/tsconfig.app.json#L7
I'm not super familiar with Angular, but it seems to be set up to look to tsconfig.app.json rather than the root tsconfig.json. When the "types" attribute is set in a tsconfig.json file, only the third-party types specified get loaded. So since "types" is an empty list, no DefinitelyTyped modules get loaded automatically.
There are two ways you can resolve this: (1) remove the "types" property, in which case everything in node_modules/@types will be loaded, or (2) specify the segment library explicitly ("types": ["segment-analytics"]). Hope that works!
For more details, see https://www.typescriptlang.org/docs/handbook/tsconfig-json.html#types-typeroots-and-types.
Nice find. That worked. And thanks for increasing my knowledge of typescript a little bit.
I'm closing this issue now as it's resolved. But I did have a question - is there any advantage of having files with global declarations? It seems it's better to have everything as modules so that things are explicitly imported and no chance of conflict between different globally declared variables that have same name.
Just leaving a comment here for folks who might end up here from Google.
I had similar problems to the above with a Vue app created using the Vue CLI.
Trying to use analytics without an import gave me Cannot find name 'analytics'.
Trying to import analytics from 'segment-analytics'; got me File '<SNIP>/node_modules/@types/segment-analytics/index.d.ts' is not a module.
Using @fongandrew's tips above, I looked at the types property in tsconfig.js and found it was ["webpack-env"]. Adding segment-analytics to this list (so now it's ["webpack-env", "segment-analytics"]) did the trick.
@GrahamLea It's still not clear to me how the issue is solved. Do I still use import analytics from 'sgement-analytics'?
@hisham I agree that it is better to export types. As it is right now, I have a hard time making a wrapper function for these segment calls with proper types in Typescript.
@arvinsim It's been a while, so I don't remember exactly, but looking at my code I have no imports for segment-analytics, so I am guessing that adding it to types makes those types available in every .ts file. 🤷🏻♂️
@arvinsim I can confirm that just adding the additional string "segment-analytics" to the types property in tsconfig.js fixed the issue globally in my project. I no longer see errors when calling analytics.track for example even when analytics hasn't been explicitly imported to the file.
Most helpful comment
It looks like the problem is here: https://github.com/hisham/angular-segment-test/blob/master/src/tsconfig.app.json#L7
I'm not super familiar with Angular, but it seems to be set up to look to tsconfig.app.json rather than the root tsconfig.json. When the "types" attribute is set in a tsconfig.json file, only the third-party types specified get loaded. So since "types" is an empty list, no DefinitelyTyped modules get loaded automatically.
There are two ways you can resolve this: (1) remove the "types" property, in which case everything in
node_modules/@typeswill be loaded, or (2) specify the segment library explicitly ("types": ["segment-analytics"]). Hope that works!For more details, see https://www.typescriptlang.org/docs/handbook/tsconfig-json.html#types-typeroots-and-types.