How to Use Generic Types to Get Object with Same Type

How to use generic types to get object with same type

Update: For a better solution, see Rob's answer.


Similarly as in How can I create instances of managed object subclasses in a NSManagedObject Swift extension?,
this can be done with a generic helper method:

extension NSManagedObject {

func transferTo(context context: NSManagedObjectContext) -> Self {
return transferToHelper(context: context)
}

private func transferToHelper<T>(context context: NSManagedObjectContext) -> T {
return context.objectWithID(objectID) as! T
}
}

Note that I have changed the return type to Self.
objectWithID() does not return an optional
(in contrast to objectRegisteredForID(), so there is no need to
return an optional here.

Update: Jean-Philippe Pellet's suggested
to define a global reusable function instead of the helper method
to cast the return value to the appropriate type.

I would suggest to define two (overloaded) versions, to make this
work with both optional and non-optional objects (without an unwanted
automatic wrapping into an optional):

func objcast<T>(obj: AnyObject) -> T {
return obj as! T
}

func objcast<T>(obj: AnyObject?) -> T? {
return obj as! T?
}

extension NSManagedObject {

func transferTo(context context: NSManagedObjectContext) -> Self {
let result = context.objectWithID(objectID) // NSManagedObject
return objcast(result) // Self
}

func transferUsingRegisteredID(context context: NSManagedObjectContext) -> Self? {
let result = context.objectRegisteredForID(objectID) // NSManagedObject?
return objcast(result) // Self?
}
}

(I have updated the code for Swift 2/Xcode 7. The code for earlier
Swift versions can be found in the edit history.)

Generic Types: Dynamically type check specific object properties: using key ~ value pair from Type A, in Generic Type B

You can generate a union for each possible key : BatchObj<"Text"> | BatchObj<"Note"> | BatchObj<"Number"> using the distribution of conditional types. This will ensure the relationship between fieldType and properties is preserved:

type BatchObj<K extends keyof FieldTypeProperties> = {
fieldType: K;
properties: FieldTypeProperties[K];
}
type BatchObjUnion<K extends keyof FieldTypeProperties = keyof FieldTypeProperties> = K extends K ? BatchObj<K>: never

// Intended Usage:
let testBatch1: BatchObjUnion[] = [
{
fieldType: supportedFields.Text,
properties: { text: "" }, //Type A
},
]

Playground Link

Instantiate a generic type with every key of a different type

The solution is to make TypeScript use each constituent of keyof Foo separately:

type FooPropertyData<T extends keyof Foo = keyof Foo> = T extends T ? FooProperty<T> : never;

This phenomenon can be demonstrated in the following:

type All<T> = Set<T>;

type Each<T> = T extends T ? Set<T> : never;

type AllIn = All<"1" | "2">; // Set<"1" | "2">

type EachIn = Each<"1" | "2">; // Set<"1"> | Set<"2">

Playground

Get the type of a specific key of generic type in TypeScript

You can add a constraint on U so that it is a subset of the keys of T using extends. V could represent the updateKey type and also have the same constraint.

Simplifying your problem to a funtion updateObject instead of updateArray it would become:

function updateObject<
T,
U extends keyof T,
V extends keyof T,
>(
obj: T,
testKey: U,
testValue: T[U],
updateKey: V,
updateValue: T[V],
testFailValue?: T[V]
) {
if (obj[testKey] === testValue) {
obj[updateKey] = updateValue;
} else if (testFailValue !== undefined) {
obj[updateKey] = testFailValue;
}
}

updateObject({aString: 'hello', aNumber: 42}, 'aString', 'hello', 'aNumber', 23);

Generic type for object values

If I understand you correctly what you need is

function f<T>(input: {[P in keyof T]: Fn<T[P]>}): void {

Two equivalent generic types are treated differently in function body

The type InferredPermittedKeysError is defined as a subtype of keyof PermittedObj, and PermittedObj is any subtype of Pick<AllKeysObj, GoodKeys>, which means it can have arbitrary properties in addition to GoodKeys. So InferredPermittedKeysError could be an arbitrary property name, it does not have to be a subtype of GoodKeys.

For example:

  • Suppose AllKeysObj is just equal to AnObjectWithAllKeys.
  • Suppose also that PermittedObj is the type {goodkey1: string, goodkey2: string, foobar: string}, which is indeed a subtype of Pick<AllKeysObj, GoodKeys>.
  • Then keyof PermittedObj would be the union type 'goodkey1' | 'goodkey2' | 'foobar'.
  • Then suppose InferredPermittedKeysError is the type 'foobar', which is allowed because it is a subtype of that union type.
  • Of course, 'foobar' does not extend GoodKeys.

In contrast, InferredPermittedKeysNOError must indeed be a subtype of GoodKeys, because it is defined as a subtype of keyof Pick<AllKeysObj, GoodKeys> which is equal to the type GoodKeys. So in fact your two generic types are not equivalent; your Id function is giving the wrong result here.

To confirm, the following call is allowed, with no type errors:

// const test: ['foobar', 'goodkey1']
const test = genericFunction<
AnObjectWithAllKeys,
{goodkey1: string, goodkey2: string, foobar: string},
'foobar',
'goodkey1'
>();

Playground Link

Generic class with two parameters

You need two types of Geneerics:

public class Duo<P,Q> {

P first;
Q second;

public Duo(P one , Q two) {
this.first = one;
this.second = two;
}
}

Later you can use as follows:

Duo<String, Integer> duo = new Duo<>("Hello", 5);


Related Topics



Leave a reply



Submit