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

Object Key references extraction constrain for spread, returning key array of type string. #25670

Closed
wesleyolis opened this issue Jul 15, 2018 · 9 comments
Labels
Duplicate An existing issue was already created

Comments

@wesleyolis
Copy link

wesleyolis commented Jul 15, 2018

Basically this would allow one to constrain the selection of keys, to belong to that of an existing type.

It is dependent on the following functionality not yet available yet, that has been proposal/requested:

KeyRef implementation

DropFirstParam<T>  = T extends [ParamA : any, ...args : infer T] ? T : never

type KeysOfObjectRecusive<O extends {}, S extends any : []> S extends [infer K extends keyof O, ...args[]] ?
[K, keyof O[K] | undefined]
: [K, KeysOfObjectRecusive<O[K], DropFirstParam<S>>]

   type KeysOfObject<S extends any : []> S extends [infer K extends keyof O, ... args : any []] ? [K, keyof O[K]] : [K]

function KeyRef<O extends {}, S extends KeysOfObjectRecusive<O,S>(object  : O, K : S) : string []
{
    const keys = string [];
    let i = arguments.length - 1;
    while(i--)
        keys.push(arguments[i]);

    return keys;
}

Examples of usage of keyRef:

KeyRef(object, 'KeyA', 'KeyAB','KeyABC') // valid
KeyRef(object, 'KeyB', 'KeyBC','KeyBCB') // valid
KeyRef(object, 'KeyC', 'KeyCC','KeyCCA')


KeyRef(object, 'KeyA', 'KeyBA','KeyBAC')    // invalid
KeyRef(object, 'KeyB', 'KeyAB','KeyABC')    // invalid
    

Templating the constraint as of 2018.07.12, results in error

// Error, Key A only refers to a type but is being used here as a value.
function KeyRef<O extends {}, KeyA extends keyof O, keyB extends O[KeyA]>(object  : O, KeyA : KeyA, KeyB : KeyB) : string []
function KeyRef<O extends {}, KeyA extends keyof O>(object  : O, K : KeyA) : string []
function KeyRef<O extends {}, S extends any>(object  : O, Keys : any) : string []

Examples dependancies structure

const object = {
    keyA : {
        KeyAA : {
            KeyAAA : 'AAA',
            KeyAAB : 'AAB',
            KeyAAC : 'AAC'
        },

        KeyAB : {
            KeyABA : 'ABA',
            KeyABB : 'ABB',
            KeyABC : 'ABC'
        },
        KeyAC : {
            KeyACA : 'ACA',
            KeyACB : 'ACB',
            KeyACC : 'ACC'
        }
    },
    KeyB : {
        KeyBA : {
            KeyBAA : 'BAA',
            KeyBAB : 'BAB',
            KeyBAC : 'BAC'
        },

        KeyBB : {
            KeyBBA : 'BBA',
            KeyBBB : 'BBB',
            KeyBBC : 'BBC'
        },
        KeyBC : {
            KeyBCA : 'BCA',
            KeyBCB : 'BCB',
            KeyBCC : 'BCC'
        }
    }
    ,
    KeyC : 
    {
        KeyCA : {
            KeyCAA : 'CAA',
            KeyCAB : 'CAB',
            KeyCAC : 'CAC'
        },

        KeyCB : {
            KeyCBA : 'CBA',
            KeyCBB : 'CBB',
            KeyCBC : 'CBC'
        },
        KeyCC : {
            KeyCCA : 'CCA',
            KeyCCB : 'CCB',
            KeyCCC : 'CCC'
        }
    }

}
@jcalz
Copy link
Contributor

jcalz commented Jul 15, 2018

This works today (in TS3.0, typescript@next):

type ObjectFromKeyArray<T extends any[]> =
  ((...xr: T) => void) extends ((x: infer H, ...r: infer R) => void) ?
  H extends keyof any ? { [K in H]: ObjectFromKeyArray<R> } : never : any

function KeyRef<S extends string[]>(object: ObjectFromKeyArray<S>, ...k: S): string[] {
  return k.reverse();
}

KeyRef(object, 'keyA', 'KeyAB', 'KeyABC') // valid
KeyRef(object, 'KeyB', 'KeyBC', 'KeyBCB') // valid
KeyRef(object, 'KeyC', 'KeyCC', 'KeyCCA') // valid

KeyRef(object, 'keyA', 'KeyBA', 'KeyBAC') // invalid
KeyRef(object, 'KeyB', 'KeyAB', 'KeyABC') // invalid

@wesleyolis
Copy link
Author

@jcalz Hey, Thanx I just copy and pasted see it works as or 20180712. Great!!
I see that you took a different approach and reversed the constraint, rather created constraint for the object to meat from the keys, instead of the approach I took to create a constraint for the keys to meet. Nice!

I will also remember that see to be able to call your the type recursively, when inside of {[k in H] :...}
which is what I discovered with our JoiX work, were I need to do mutiple passes and extract types..
Of which my final approach is basically, a little instruction set for the recursive patten.

You don't possibly know of a way, that I could basically have an intermediate type function that being use recursively, that could be over written from parent file, which could the enhance the logic. Like plug in middle ware for type processing extraction for our JoiX work.

type extenable<T> = T // the type I would like to replace, or passing in as a function for processing, which be external file.

type extractInternal<O> = {
[K in O] : O[K] extends ... ? extenable<O[K]>..
}

@wesleyolis
Copy link
Author

@jcalz Advantage of the forward method versus the reverse method would be the following:

  • Compiler could communicate, which key was invalid, if one wasn't paying attention to the the object param, which would allow for better compiler error to be emited
  • The IDE would be able to provide auto complete suggestions, if structured in a forward direction, versus a reverse direction.

So I guess their is still a few advantages to having the forward direction, work.

@jcalz
Copy link
Contributor

jcalz commented Jul 16, 2018

Yeah, my suggestion was just a workaround in the absence of general-purpose recursive type functions.

Another workaround would be to break your single function apart into more of a builder pattern:

class KeyRef<T> {
    constructor(private object: T, private keys: string[] = []) {
    }
    k<K extends string & keyof T>(k: K) {
        return new KeyRef(this.object[k], [k].concat(this.keys as any))
    }
    build() {
        return this.keys;
    }
}

new KeyRef(object).k('keyA').k('KeyAB').k('KeyABC').build() // valid
new KeyRef(object).k('KeyB').k('KeyBC').k('KeyBCB').build() // valid
new KeyRef(object).k('KeyC').k('KeyCC').k('KeyCCA').build() // valid

new KeyRef(object).k('keyA').k('KeyBA').k('KeyBAC').build() // invalid, 'KeyBA'
new KeyRef(object).k('KeyB').k('KeyAB').k('KeyABC').build() // invalid, 'KeyAB'

@wesleyolis
Copy link
Author

@jcalz Hey, yip could do that too, just prefer their only being single common between values, less verbose. But we do make use of the builder patten in our enhance joiX, which gets us around a .d.ts file type generation bug, were we got clipped saying out type definition was recursive, causing us to manually compose the types in the d.ts file. Just need finished current work, then swap out the implementation with improved implementation, using builder pattern, which is not subjective to the recusive detection and strip, kick out for any.

On another note, I am trying to devise a simple syntax for the following, were morph the type on assignment, for like c++ pointer style stuff of referances to store results in instead of the return type.
It also I think would lend its self better to the spread operator, which become complicated if want a totally dynamic spread operator definition, based on current input and future add values.

// Return type form typespesification for out, that it would have only after use.
// basically means I need to infer a type, that is an extract from my current type, that can be used after wards.
// this means I need the concept of an in and out, which is the sam as what I require for the spread operator.

// Ran into this problem to day, want to communicate the property that is going is going to be assigned to, but I can only ensure it exists for assignment. So starting think around the lines of this and think that also fell similar in line with the spread operator.
const obj = {propA: 234};
test(obj);
// After the use of the function, we have infer a type modification, so that
// we can use the resulting infer enhanced referance of the return result out the function()
function test<T in extends {}, T out extends T & {prop? :'sdf'}>(param : T ) : any/
function test<T extends {}, T = extends T & {prop? :'sdf'}>(param : T )
{
}

type Extends<K, O extends any [], I extends number = 0> = K extends keyof O[I] ? Extends<O[I + 1 ], O[K], I++> : never

type Contraint<K, O extends any [], I extends number = 0> = [K extends keyof O[I] ? K : never, Contraint<O[I + 1 ], O[K], I++>]


function spreadTypeInferance<O extends {}, T in extends Extends<T[0], [O , ... T]>, T out = Extends<T[0], [O, ... T]>>(obj : O, ... args : T)
function spreadTypeInferance<O extends {}, T extends Extends<T[0], [O , ... T]>, T = Extends<T[0], [O, ... T]>>(obj : O, ... args : T)
{
}

@jcalz
Copy link
Contributor

jcalz commented Jul 17, 2018

I think you might be asking for #17760. I must admit I have a hard time understanding your posts; you may want to look into free courses for improving your writing skills, like this one.

@wesleyolis
Copy link
Author

@jcalz Which dialect would you like me to know better... ish...

@mhegazy
Copy link
Contributor

mhegazy commented Jul 19, 2018

looks like a duplicate of #25719

@mhegazy mhegazy added the Duplicate An existing issue was already created label Jul 19, 2018
@typescript-bot
Copy link
Collaborator

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

4 participants