Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support string types in dynamic import types. #32705

Open
5 tasks done
everett1992 opened this issue Aug 4, 2019 · 4 comments
Open
5 tasks done

Support string types in dynamic import types. #32705

everett1992 opened this issue Aug 4, 2019 · 4 comments
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript

Comments

@everett1992
Copy link

Search Terms

dynamic import type

Suggestion

I'd like to define a function that wraps a dynamic require or import. Specifically, I'm writing a library that uses node's worker_thread and I want to support 'dependency injection' via dynamic import. You can't pass class instances or functions across execution contexts.

import { isMainThread, Worker, workerData } from 'worker_threads';

export type LoggerImportPath = ???;

export default function lib(logger: LoggerImportPath): Worker {
  return new Worker(__filename, { workerData: { logger });
}

if (!isMainThread) {
  const module: { default: Logger } = await import(workerData.logger);
}
// no type error, ./path/to/logger exports a Logger.
lib(`${__filename}/path/to/logger');

// this should be a type error: import('not-a-module') is not assignable to { default: Logger };
lib('not-a-module');

I don't think you can currently define LoggerImportPath any more precisely than string and import(path: string) returns Promise<any>.

// this returns `{ default: Logger}`
await import('path');

// this returns `any`
const wrapper = path => import(path);
const module: any = await wrapper('path')

Use Cases

I haven't seen this pattern in any other libraries, but I think it's a good way to pass classes or functions across threads or execution contexts. You can't currently limit the kinds of strings that library uses can pass when a module path is expected.

Examples

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.
@fatcerberus
Copy link

Regular expression types would probably solve this nicely: #6579

@everett1992
Copy link
Author

everett1992 commented Aug 5, 2019 via email

@RyanCavanaugh RyanCavanaugh added Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript labels Aug 5, 2019
@RyanCavanaugh
Copy link
Member

Open to feedback here but we'd have to have some very strong and compelling use cases to investigate further.

The process of constructing a "program" (the set of files under analysis) begins by following static constructs like import * as id from "p" to look for other files to load. Once this file-finding process finishes, the set of files is effectively closed and from there many, many assumptions are made on the basis that no new files will show up later. So this import construct would have to either be restricted to loading modules which are already part of the program (which is kind of weird) or would require an extremely large architectural rejiggering of TS itself.

@everett1992
Copy link
Author

I'll assume that dynamic import await import('foo') is included in the static constructs that you mentioned.

I can think of one way to implement this without much rejiggereing.

  • Add a typescript syntax import.later(path: string-literal) with the same static constraints as current await import(path: string-literal) (bikeshed the name later :)).
  • import.later(path: string-literal) compiles transparently to the string-literal
  • import.later(path: string-literal) has the type string & Module<T> where T is the same shape a await import(path) and Module is a marker type.
async function factory(path: string & Module<{default: Logger}>): Logger {
   return import(path).then(module => module.default);
}

factory(import.later('./path-to-module'));

This feels like a hack, so feel free to resolve this issue. IMO this pattern will become prevalent if people start authoring libraries that use web workers or worker threads, but I don't think there's much demand for the feature right now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

3 participants