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

Make this package work with generics #11

Open
hevans90 opened this issue Mar 2, 2018 · 7 comments
Open

Make this package work with generics #11

hevans90 opened this issue Mar 2, 2018 · 7 comments

Comments

@hevans90
Copy link

hevans90 commented Mar 2, 2018

I know in your README you mentioned that this doesn't work with generic types, i.e.

keys<T>

This functionality would be incredibly useful to me if it worked - and I would love to help contribute if you have any ideas for how to get this to work.

@kimamula
Copy link
Owner

kimamula commented Mar 2, 2018

This would require exhaustive analysis of every possible context under which key<T>() is called and would result in much more complex transformation than the current version.

I don't think it is feasible to provide this feature.

@robbiemu
Copy link

could you go into a bit more detail ? I'm noticing a pretty sweet use that would obviate the need to write factory methods per interface, for example:

export function factoryInterface<T>(template = <Partial<T>>{}): T {
  const keyCollection = keys<T>();
  const data = <Partial<T>>{};

  Object.keys(template)
    .filter(key => keyCollection.includes(<keyof T>key))
    .forEach(key => data[<keyof T>key] = template[<keyof T>key]);

  keyCollection.forEach(key => {
    if (!data.hasOwnProperty(key)) {
      data[key] = undefined
    }
  });

  return data as T;
}

@kimamula
Copy link
Owner

What JavaScript code do you think should be emitted as the result of the transformation of your function?
I cannot think of any reasonable output that can support every use case of such function.

@cmuentes
Copy link

cmuentes commented Feb 1, 2020

What JavaScript code do you think should be emitted as the result of the transformation of your function?
I cannot think of any reasonable output that can support every use case of such function.

The exact use case I have right now is the ability to create a property descriptor for an object so I can dynamically create a data grid by enumerating the model properties and emitting a column for each property.

Basically something like:

export class ModelDescriptor<T extends object> {
  properties: Array<string>;

  constructor() {
    const props = keys<T>();
    this.properties.push(...Object.keys(props));
  }
}

This would be super useful to me right now.

@ShadiestGoat
Copy link

Ok ok I get that this wouldn't really work, but is there a work around of sorts?

@cheruvian
Copy link

I cannot think of any reasonable output that can support every use case of such function.

I know I haven't thought about it as much as I'm sure you have, and am probably missing something super obvious here but can you please provide a couple example use cases where the outputs conflict?

Thinking out loud (and without [yet] reviewing the implementation), I wonder if there isn't a way to support some basic case(s) behind an experimental feature flag?

@kimamula
Copy link
Owner

kimamula commented Jan 2, 2024

@cheruvian For example, the JS output of the TS code in this comment would look like the following:

export function factoryInterface(template = {}) {
    const keyCollection =  /* ??? */;
    const data = {};
    Object.keys(template)
        .filter(key => keyCollection.includes(key))
        .forEach(key => data[key] = template[key]);
    keyCollection.forEach(key => {
        if (!data.hasOwnProperty(key)) {
            data[key] = undefined;
        }
    });
    return data;
}

What should the /* ??? */ part of the above code look like so that the function works for every usage? One possible solution would be to change the parameter of the function during the transpilation:

export function factoryInterface(keys, template = {}) {
    const keyCollection =  keys;
    // ...
}

const obj1 = factoryInterface(['a', 'b'], { a: 'a' }); // original code: const obj1 = factoryInterface<{ a: string; b: number }>({ a: 'a' });
const obj2 = factoryInterface(['c', 'd'], { c: true }); // original code: const obj2 = factoryInterface<{ c: boolean; d: Date }>({ c: true });

However, I don't think this is feasible:

  • Changing the function like this is dangerous and would introduce many unforeseeable issues (e.g., when accessing the function arguments in decorators)
  • What if the code containing the function is published as an npm package and used from an external code? (note that usually an npm package only contains the JS code and .d.ts files and does not have the original TS code, which is required for the transformer to work)
  • If the direct caller of the function also uses a generic type, the transformer needs to track where it's coming from and the transformation would be more complex:
// TS input
function directCaller<T>() {
    return factoryInterface<Omit<T, 'b' | 'c'>>();
}
const obj = directCaller<{ a: string; b: number, c: boolean, d: Date }>();

// JS output
function directCaller(arg) {
    return factoryInterface(arg);
}
const obj = directCaller(['a', 'd']); // All type operations along the way need to be applied somehow

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants