Skip to content

d.ts type is different than intellisense type #40003

@macdaj

Description

@macdaj

TypeScript Version: 3.9.6 (npm and in vs code)

Search Terms:
declaration intellisense

Code

export type Validator<T> = NativeTypeValidator<T> | ObjectValidator<T>

export type NativeTypeValidator<T> = (n: any) => T | undefined
export type ObjectValidator<O> = {
  [K in keyof O]: Validator<O[K]> 
}

//native validators
export const SimpleStringValidator:NativeTypeValidator<string> = (val) => typeof(val) === "string" ? val : undefined

///object validator function
export const ObjValidator = <V>(validatorObj: ObjectValidator<V>) => (o:any):V =>{
  let result = {} as V;
  //we can only validate objects
  if (typeof (o) !== "object") { return undefined; }
  const validatorKeys = Object.keys(o) as [keyof ObjectValidator<V>]
  validatorKeys.forEach((validatorKey) => { 
    const objValue = o[validatorKey] as V[keyof V];
    const objectValidator = validatorObj[validatorKey]
    if (!objectValidator) { return undefined } //do nothing if no validator exists for the key in o
    //figure out if we have a nested object validator or a native validator at the corresponding key of validatorObj
    if (typeof (objectValidator) === "object") {
      result[validatorKey] = ObjValidator(objectValidator as ObjectValidator<V[keyof V]>)(objValue)
    }
    else {
      const nativeValidator = objectValidator as NativeTypeValidator<V[keyof V]>;
      result[validatorKey] = nativeValidator(objValue)
    }
  })
  return result;
}

export const test  = {
  Test: {
    Test1: {
      Test2: SimpleStringValidator
    },
  }
}

export const validatorFunc = ObjValidator(test);
export const outputExample = validatorFunc({
  Test: {
    Test1: {
      Test2: "hi"
    },
  }
})

outputExample.Test.Test1.Test2 = "1";
outputExample.Test.Test1.Test2 = 1; //vs code intellisense complains because needs to be type string

Expected behavior:
I'm trying to extract a type mapping from an object containing native type validation function so for example, if I have the object

const test  = {
  Test: {
    Test1: {
      Test2: SimpleStringValidator //return type is string or undefined but input can be anything
    },
  }
}

I want to generate the type.

type Extracted = {
  Test: {
    Test1: {
      Test2: string
    }
  }
}

and have it be the same in intellisense and in the generated type declaration file. In the above example this generated type should be the return value type of the

Actual behavior:
Intellisense and the output declaration file are inconsistent. Intellisense creates a nested type with type string at property Test2 (this is the desired behaviour) but the output declaration file is as follows

export declare type Validator<T> = NativeTypeValidator<T> | ObjectValidator<T>;
export declare type NativeTypeValidator<T> = (n: any) => T | undefined;
export declare type ObjectValidator<O> = {
    [K in keyof O]: Validator<O[K]>;
};
export declare const SimpleStringValidator: NativeTypeValidator<string>;
export declare const ObjValidator: <V>(validatorObj: ObjectValidator<V>) => (o: any) => V;
export declare const test: {
    Test: {
        Test1: {
            Test2: NativeTypeValidator<string>;
        };
    };
};
export declare const validatorFunc: (o: any) => {
    Test: {
        Test1: any;
    };
};
export declare const outputExample: {
    Test: {
        Test1: any;
    };
};

In the declaration file the Test1 definition gets blown away to any, which leads me to not be able to directly import the generated type into other projects.

Playground Link:
The issue is reproducible in the typescript playground. Intellisense displays the Test2 property correctly as type string, where in the d.ts file Test1 is of type any. Tried with typescript versions 3.9.2 and 4.0.0-beta
Playground Link

ts compiler options:

  "compilerOptions": {
    "noImplicitAny": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "strictBindCallApply": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "alwaysStrict": true,
    "esModuleInterop": true,
    "declaration": true,
    "emitDecoratorMetadata": true,
    "moduleResolution": 2,
    "target": "ES2019",
    "module": "ESNext"
  }

Related Issues:

Metadata

Metadata

Assignees

Labels

Fix AvailableA PR has been opened for this issueNeeds InvestigationThis issue needs a team member to investigate its status.RescheduledThis issue was previously scheduled to an earlier milestone

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions