tst-reflect icon indicating copy to clipboard operation
tst-reflect copied to clipboard

How to get properties of {[key in keyof T]: T[key]} type?

Open Igorjan94 opened this issue 4 years ago • 4 comments

import { getType, Type } from "tst-reflect"
export class Temp {
    id: string
    email: string
}
type T = {
    [key in keyof Temp]: Temp[key]
}
function foo<T>() {
    const type: Type = getType<T>()
    console.log(type)
}
foo<T>()

Following code produces

this output
TypeActivator {
  _name: 'T',
  _fullName: 'T',
  _kind: 5,
  _constructors: [],
  _properties: [],
  _methods: [],
  _decorators: [],
  _typeParameters: [],
  _ctor: undefined,
  _ctorDesc: ConstructorImportActivator {},
  _interface: undefined,
  _isUnion: false,
  _isIntersection: false,
  _types: [],
  _literalValue: undefined,
  _typeArgs: [],
  _conditionalType: undefined,
  _indexedAccessType: undefined,
  _genericTypeConstraint: undefined,
  _genericTypeDefault: undefined,
  _baseType: TypeActivator {
    _name: 'Object',
    _fullName: 'Object',
    _kind: 2,
    _constructors: [],
    _properties: [],
    _methods: [],
    _decorators: [],
    _typeParameters: [],
    _ctor: [Function: ctor],
    _ctorDesc: ConstructorImportActivator {},
    _interface: undefined,
    _isUnion: false,
    _isIntersection: false,
    _types: [],
    _literalValue: undefined,
    _typeArgs: [],
    _conditionalType: undefined,
    _indexedAccessType: undefined,
    _genericTypeConstraint: undefined,
    _genericTypeDefault: undefined,
    _baseType: undefined
  }
}
As I can see it doesn't contain neither properties nor types. Is it bug or feature or do I use it in wrong way?

Does it work with Omit and Pick? (I see the same output for them because they are implemented in similar way I did it in example)

Igorjan94 avatar Mar 16 '22 11:03 Igorjan94

Hello @Igorjan94, this is an issue with the current implementation. These types are not handled properly; there is a new big upcoming update which change the way how are these types handled. It should be possible to hotfix this. I can do it in the current version if you want.

Hookyns avatar Mar 16 '22 11:03 Hookyns

Thank you @Hookyns! No need to hotfix, I'll wait for update, it works fine in my current scenario, I was just testing how it works on different types I have

Igorjan94 avatar Mar 16 '22 11:03 Igorjan94

@Hookyns is the future changes on the devel branch? I have a potential fix to this problem along with a couple more changes. When I get time I can port those fixes to the devel branch.

joeferner avatar May 21 '22 15:05 joeferner

@joeferner it can be main, I do just minor fixes in the main and devel cuz I work on the next version which change the transformer a lot.

Hookyns avatar May 21 '22 18:05 Hookyns

@Igorjan94, @joeferner Should be fixed in [email protected] and [email protected].

Hookyns avatar Aug 14 '22 12:08 Hookyns

Works like a charm!

Only bug I found during some testing, that Omit fails when using [key: string]: any extension in class:

function foo<T>() {
    const type: Type = getType<T>()
    console.log(type)
}

export class Temp {
    id?: string
    email?: string
//    [key: string]: any //  <--------
}

foo<Omit<Temp, 'email'>>()
Example output
TypeActivator {
  _name: 'Omit',
  _fullName: 'Omit',
  _kind: 5,
  _constructors: [],
  _properties: [],
  _indexes: [
    IndexInfo {
      _keyType: [LazyType],
      _type: [LazyType],
      readonly: false
    },
    IndexInfo {
      _keyType: [LazyType],
      _type: [LazyType],
      readonly: false
    }
  ],
  _methods: [],
  _decorators: [],
  _typeParameters: [],
  _ctor: undefined,
  _ctorDesc: ConstructorImportActivator {},
  _interface: undefined,
  _isUnion: false,
  _isIntersection: false,
  _types: [],
  _literalValue: undefined,
  _typeArgs: [],
  _conditionalType: undefined,
  _indexedAccessType: undefined,
  _functionType: undefined,
  _genericTypeConstraint: undefined,
  _genericTypeDefault: undefined,
  _isGenericType: false,
  _genericTypeDefinition: undefined,
  _baseType: LazyType {
    typeResolver: [Function (anonymous)],
    resolvedType: TypeActivator {
      _name: 'Object',
      _fullName: 'Object',
      _kind: 2,
      _constructors: [],
      _properties: [],
      _indexes: [],
      _methods: [],
      _decorators: [],
      _typeParameters: [],
      _ctor: [Function: ctor],
      _ctorDesc: ConstructorImportActivator {},
      _interface: undefined,
      _isUnion: false,
      _isIntersection: false,
      _types: [],
      _literalValue: undefined,
      _typeArgs: [],
      _conditionalType: undefined,
      _indexedAccessType: undefined,
      _functionType: undefined,
      _genericTypeConstraint: undefined,
      _genericTypeDefault: undefined,
      _isGenericType: false,
      _genericTypeDefinition: undefined,
      _baseType: undefined
    }
  }
}

If marked line is uncommented, then type is wrong, otherwise $-$ works as intended

Igorjan94 avatar Aug 15 '22 16:08 Igorjan94

Okay,.. I think I got it.

It works like intended IMHO because Omit creates new type alias which is object {} and that object have some properties but it has indexer [key: string]: any too which hides all other properties with string key. Those properties are subset of that indexer so TypeScript throws it away, because it is redundant information.

Try this:

function foo<T>() {
	const type: Type = getType<T>()
	console.log(type)
}

export class Temp {
	id?: string
	email?: string
	[key: symbol]: any //  <-------- SYMBOL key
}

foo<Omit<Temp, 'email'>>()

Now it generates the id property, because it is not hidden under indexer anymore, because it does not match key type. Generated metadata for that:

{
	n: "Omit",
	k: 5,
	props: [{
		n: "id",
		t: _ßr.Type.store.wrap({
			n: "string",
			k: 2,
			ctor: function () {
				return Promise.resolve(String);
			}
		}),
		am: 2,
		acs: 0,
		ro: false,
		o: true
	}],
	indxs: [{
		k: _ßr.Type.store.wrap({
			n: "symbol",
			k: 2,
			ctor: function () {
				return Promise.resolve(Symbol);
			}
		}),
		t: _ßr.Type.store.wrap({
			n: "any",
			k: 2
		}),
		ro: false
	}]
}

Hookyns avatar Aug 15 '22 17:08 Hookyns

Ah yeah, I see. Didn't notice that typescript throws it away in indexer (anyway, I don't use these indexers now, it was added for migrating)

Thanks for making this amazing package, really appreciate the effort!

Igorjan94 avatar Aug 15 '22 18:08 Igorjan94