analytics
analytics copied to clipboard
TypeScript support for @analytics/google-tag-manager
I am using @analytics/google-tag-manager. I saw analytics have TypeScript support while @analytics/google-tag-manager not.
When add
import googleTagManager from '@analytics/google-tag-manager';
got
TS7016: Could not find a declaration file for module '@analytics/google-tag-manager'. '/hongbomiao.com/client/node_modules/@analytics/google-tag-manager/lib/analytics-plugin-google-tag-manager.cjs.js' implicitly has an 'any' type. Try
npm install @types/analytics__google-tag-manager
if it exists or add a new declaration (.d.ts) file containingdeclare module '@analytics/google-tag-manager';
It would be great to add TypeScript in future. Thanks! : )
I would like to improve TS support for browser & node packages.
Running into some issues where the node/browser types are different but TS only allows for one type definition & doesn't follow the module resolution to grab the proper types.
From what I can tell, this might require publishing 2 separate packages. 1 for browser and 1 for serverside node... This is a bummer because it will double the amount of maintenance/packages for the project 😅
The solution proposed looks like a lot of work...
Will need to think on this one
As a workaround I added this code in a Types.ts file
declare module '@analytics/google-analytics' {
type GoogleAnalyticsOptions = {
/** Google Analytics site tracking Id */
trackingId:string;
/** Enable Google Analytics debug mode */
debug?:boolean;
/** Enable Anonymizing IP addresses sent to Google Analytics. See details below */
anonymizeIp?:boolean;
/** Map Custom dimensions to send extra information to Google Analytics. See details below */
customDimensions?:object;
/** Reset custom dimensions by key on analytics.page() calls. Useful for single page apps. */
resetCustomDimensionsOnPage?:object;
/** Mapped dimensions will be set to the page & sent as properties of all subsequent events on that page. If false, analytics will only pass custom dimensions as part of individual events */
setCustomDimensionsToPage?:boolean;
/** Custom tracker name for google analytics. Use this if you need multiple googleAnalytics scripts loaded */
instanceName?:string;
/** Custom URL for google analytics script, if proxying calls */
customScriptSrc?:string;
/** Additional cookie properties for configuring the ga cookie */
cookieConfig?:object;
/** Set custom google analytic tasks */
tasks?:object;
};
type AnalyticsPlugin = {
/** Name of plugin */
name: string;
/** exposed events of plugin */
EVENTS?: any;
/** Configuration of plugin */
config?: any;
/** Load analytics scripts method */
initialize?: (...params: any[]) => any;
/** Page visit tracking method */
page?: (...params: any[]) => any;
/** Custom event tracking method */
track?: (...params: any[]) => any;
/** User identify method */
identify?: (...params: any[]) => any;
/** Function to determine if analytics script loaded */
loaded?: (...params: any[]) => any;
/** Fire function when plugin ready */
ready?: (...params: any[]) => any;
};
function GoogleAnalytics (options:GoogleAnalyticsOptions):AnalyticsPlugin;
export default GoogleAnalytics;
}
What can we do to help @DavidWells? It's unclear what the solution is here since it looks like the typescript files in this repo are generated.
I'm stuck on the issue where types can be different between server and client implementations
https://twitter.com/DavidWells/status/1340059740672442368
It seems like this isn't supported in typescript and this is a real bummer
Just some ideas: the way I've seen other libraries do it is to have a separate folders. If the code is completely different between the 2 this makes a lot of sense - then the programmer just imports the one that they want. e.g.
import Analytics from '@analytics/google-analytics/browser';
// or
import Analytics from '@analytics/google-analytics/node';
I've also seen libraries have a single class but they want to restrict some functions*. So you would have a single class, but export it twice with different types so that the programmer can choose what makes sense for them. e.g.
/** This is stuff that is shared to both node.js and browser */
type CommonModules = {
GenericFunc: () => void;
}
/** Browser specific definitions */
type BrowserModules = CommonModules & {
BrowserFunc: () => void;
}
/** Server specific definitions */
type ServerModules = CommonModules & {
ServerFunc: () => void;
}
/** Write all your code */
const AllMyCode:BrowserModules&ServerModules = {
GenericFunc: () => {},
BrowserFunc: () => {},
ServerFunc: () => {}
};
/** Restricted set of browser functions */
const BrowserCode = AllMyCode as BrowserModules;
export BrowserCode;
/** Restricted set of server functions */
const ServerCode = AllMyCode as ServerModules;
export ServerCode;
// Inside user code, they import which one they want
import { BrowserCode, ServerCode } from '@analytics/google-analytics';
Dunno if this helps or not
So you would have a single class, but export it twice with different types so that the programmer can choose what makes sense for them. e.g.
Since you're not using typescript internally, there's something to be said here; you're NOT using typescript internally so it's ultimately about exporting types so users can use it. You could export a Browser Type, Node Type, and union type and have your functions use the union types. Being less strict is not ideal, but then you're allowing us to cast to the more specific types when we need to, which solves our issue much more than having no type at all.
bump
The original question was about @analytics/google-tag-manager
. And although @GeoMarkou posted a really good workaround for @analytics/google-analytics
, those types are a little bit different.
Here are the types for @analytics/google-tag-manager
package:
declare module '@analytics/google-tag-manager' {
type AnalyticsPlugin = import('analytics').AnalyticsPlugin;
type GoogleTagManagerConfig = {
auth?: string;
containerId: string;
customScriptSrc?: string;
dataLayerName?: string;
debug?: boolean;
execution?: string;
preview?: string;
};
function googleTagManager(config: GoogleTagManagerConfig): AnalyticsPlugin;
export default googleTagManager;
}
The original question was about
@analytics/google-tag-manager
. And although @GeoMarkou posted a really good workaround for@analytics/google-analytics
, those types are a little bit different.Here are the types for
@analytics/google-tag-manager
package:declare module '@analytics/google-tag-manager' { type AnalyticsPlugin = import('analytics').AnalyticsPlugin; type GoogleTagManagerConfig = { auth?: string; containerId: string; customScriptSrc?: string; dataLayerName?: string; debug?: boolean; execution?: string; preview?: string; }; function googleTagManager(config: GoogleTagManagerConfig): AnalyticsPlugin; export default googleTagManager; }
where do you insert that code? or GeoMarkou declare?
@santyas You simply put it to something.d.ts
file anywhere in your project and TypeScript will pick it up.