docsforadobe / Types-for-Adobe

TypeScript types for Adobe: AfterEffects, Animate, Audition, Illustrator, InDesign, Photoshop, Premiere, ScriptUI.
517 stars 124 forks source link

TypeScript: can't find built-in utility types if "noLib": true #106

Open johhnry opened 1 year ago

johhnry commented 1 year ago

Hi,

Thanks for making those type definitions, this is really helpful to be able to use TypeScript for Adobe products!

This is a sample config for tsconfig.json and package.json:

// tsconfig.json
{
  "compilerOptions": {
    "module": "none",
    "noLib": true,
    "types": ["types-for-adobe/Photoshop/2015.5"],
    "strict": true,
    "target": "ES3",
    "downlevelIteration": true
  },
  "include": ["src/**/*"]
}
// package.json
{
  // ...
  "dependencies": {
    "types-for-adobe": "^7.0.12",
    "typescript": "^4.9.5"
  }
}

I found that this minimal example compiles but raise a TypeScript error during compilation:

// error TS2318: Cannot find global type 'Extract'.
function fn<T>(object: T) {
  for (const key in object) {}
}

// Note that this works:
function fn(object: any) {
  for (const key in object) {}
}

I don't know the internals of how the for ... in gets compiled using generics but it made me realize that I can't use any of the utility types when using "noLib": true in tsconfig.json:

// error TS2304: Cannot find name 'Extract'.
type T0 = Extract<"a" | "b" | "c", "a" | "f">;

This is because utility types are located in lib.es5.d.ts therefore not included with noLib.

I can still copy the Extract type definition in my project but maybe we can include them in the repository?

// Compiles fine
type Extract<T, U> = T extends U ? T : never;

function fn<T>(object: T) {
  for (const key in object) {}
}

Thanks!

YoungQi9754 commented 1 year ago

I also encountered this problem, then I tried removing noLib:true and adding ["ES5", "ES2015", "ES2016", "ES2017", "ES2018", "ES2019", "ES2020", "ES2021", "ESNext"] in lib, manually removing dependencies on lib.dom, but keeping lib.es*, you can try this and see if it helps you. That would be the best outcome if it works for you.

YoungQi9754 commented 1 year ago

{
  "compilerOptions": {
    "lib": [
      "ES5",
      "ES2015",
      "ES2016",
      "ES2017",
      "ES2018",
      "ES2019",
      "ES2020",
      "ES2021",
      "ESNext"
    ]
  }
}
YoungQi9754 commented 1 year ago

I guess the reason is the 'noLib:true' setting in the tsconfig.json file of the declaration file. This setting can exclude the default dependency of lib.dom, because Document is overridden in the .d.ts file. By excluding lib.dom, there won't be any errors when writing declarations. However, after setting this, 'noLib:true' must also be set in the tsconfig.json of your own project, or you need to manually exclude lib.dom.

This is my own understanding. If there is an official response, please let me know if I am right or wrong, or suggest a better way to set it up.

Thank you.

johhnry commented 1 year ago

I also encountered this problem, then I tried removing noLib:true and adding ["ES5", "ES2015", "ES2016", "ES2017", "ES2018", "ES2019", "ES2020", "ES2021", "ESNext"] in lib, manually removing dependencies on lib.dom, but keeping lib.es*, you can try this and see if it helps you. That would be the best outcome if it works for you.

The issue with adding those to the lib array is that the declarations conflict with the ones provided by types-for-adobe, typically:

node_modules/types-for-adobe/shared/JavaScript.d.ts:4:1 - error TS6200: Definitions of the following identifiers conflict with those in another file: Object, Array, Math, Date, Function, String, Number, Boolean, RegExp, Error, Partial, Readonly, Pick, Record
utenma commented 1 year ago

you can safely add the following from lib.es5.d.ts into a typescript.d.ts

type ArrayLike<T> = {
  readonly length: number;
  readonly [n: number]: T;
}

/**
* Make all properties in T optional
*/
type Partial<T> = {
  [P in keyof T]?: T[P];
};

/**
* Make all properties in T required
*/
type Required<T> = {
  [P in keyof T]-?: T[P];
};

/**
* Make all properties in T readonly
*/
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

/**
* From T, pick a set of properties whose keys are in the union K
*/
type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

/**
* Construct a type with a set of properties K of type T
*/
type Record<K extends keyof any, T> = {
  [P in K]: T;
};

/**
* Exclude from T those types that are assignable to U
*/
type Exclude<T, U> = T extends U ? never : T;

/**
* Extract from T those types that are assignable to U
*/
type Extract<T, U> = T extends U ? T : never;

/**
* Construct a type with the properties of T except for those in type K.
*/
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

/**
* Exclude null and undefined from T
*/
type NonNullable<T> = T & {};

/**
* Obtain the parameters of a function type in a tuple
*/
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;

/**
* Obtain the parameters of a constructor function type in a tuple
*/
type ConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (...args: infer P) => any ? P : never;

/**
* Obtain the return type of a function type
*/
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;

/**
* Obtain the return type of a constructor function type
*/
type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;
johhnry commented 1 year ago

@mindlid Yes sure! Or only copy the ones you need in your project (like Extract above) ^^