Using a type builder I am OK with creating a type dynamically. Would it be possible to do this from an anonymous type?
I have this so far;
//CreateType generates a type (so that I can set it's properties once instantiated) from an //anonymous object. I am not interested in the initial value of the properties, just the Type.
Type t = CreateType(new {Name = "Ben", Age = 23});
var myObject = Activator.CreateInstance(t);
Is it possible to now use Type "t" as a type argument?
My method:
public static void DoSomething<T>() where T : new()
{
}
I would like to call this method using the dynamically created Type "t". So that I can call;
DoSomething<t>(); //This won't work obviously
Yes, it is possible. To use the type as a type argument, you need to use the MakeGenericType method:
// Of course you'll use CreateType here but this is what compiles for me :)
var anonymous = new { Value = "blah", Number = 1 };
Type anonType = anonymous.GetType();
// Get generic list type
Type listType = typeof(List<>);
Type[] typeParams = new Type[] { anonType };
Type anonListType = listType.MakeGenericType(typeParams);
// Create the list
IList anonList = (IList)Activator.CreateInstance(anonListType);
// create an instance of the anonymous type and add it
var t = Activator.CreateInstance(anonType, "meh", 2); // arguments depending on the constructor, the default anonymous type constructor just takes all properties in their declaration order
anonList.Add(t);
Related
I'm trying to create a type that defines the value based on the key. If the key extends $${string} (e.g. $foo) the value should be the key without the prefix e.g. foo. If the key doens't extend $${string} (e.g. boo) the values should be null.
Example
const example = {
$foo: 'foo',
boo: null,
}
Here is an isolated example I created to get it done - but it doesn't work as intended when I apply it to the code below. 😕
type Value<T> = T extends `$${infer I}` ? I : null
type ExampleA = Value<'$foo'> // type is 'foo'
type ExampleB = Value<'boo'> // type is null
My current code
type Values = {
[K in string]: K extends `$${infer p}` ? p : null;
}
const values = {
$foo: 'foo', // Type 'string' is not assignable to type 'null'.
foo: null,
$boo: 'boo', // Type 'string' is not assignable to type 'null'.
boo: null,
} satisfies Values;
type Expected = {
readonly $foo: 'foo',
readonly foo: null,
readonly $boo: 'boo',
readonly boo: null,
}
The satisfies Values is used to infer the type later on. Similar approach is acceptable🙂
Thanks for your help and time - cheers
The problem with your Values type is that mapped types over string do not behave the way you expect them to. While conceptually you can think of string as the infinite union of all possible string literal types, a mapped type over string does not even try to iterate over every possible string literal type; it just maps one thing: string:
type Values = {
[K in string]: K extends `\$${infer S}` ? S : null;
}
/* type Values = {
[x: string]: null;
} */
And since string does not extend `\$${infer S}`, then the property type for the string key is null.
This is working as intended, as discussed in microsoft/TypeScript#22509. Mapped types over string are not what you want.
And unfortunately there is no way to write a specific type in TypeScript which behaves the way you want. The closest you could get is something like
type Values = {
[k: `\$${string}`]: string;
[k: string]: string | null;
}
using a template string pattern index signature, but the parts where the property value string needs to match the part after the "$" character (not just string) and the part where other keys need to have a null (not just string | null) cannot be represented:
const values = {
$foo: 'foo',
foo: null,
$boo: 'boo',
boo: null,
$oops: null, // error, not string
oops: 'hmm', // should be error, but isn't!
$whoops: 'oops', // should be error, but isn't!
} satisfies Values;
So we have to give up on the approach using the satisfies operator, because there is no appropriate Values type to use it with.
What you really care about is having the type of values inferred by the compiler but still checked against your desired constraint. We can get behavior like this by replacing satisfies Values with a generic helper function we can call satisfiesValues(). At runtime this function just returns its input, but the compiler can use it to validate the object literal passed in. So instead of const values = {...} satisfies Values; you would write const values = satisfiesValues({...});.
Here's one possible implementation:
const satisfiesValues = <K extends PropertyKey>(
val: { [P in K]: P extends `\$${infer S}` ? S : null }
) => val;
The function is generic in K, the keys of the val value passed in. This will most likely be some union of known keys (none of which will be just string), and then the mapped type behaves as desired:
const values = satisfiesValues({
$foo: 'foo',
foo: null,
$boo: 'boo',
boo: null,
$oops: null, // error, not "oops"
oops: 'hmm', // error, not null
$whoops: 'oops', // error, not "whoops"
});
/* const values: {
foo: null;
$foo: "foo";
boo: null;
$boo: "boo";
oops: null;
$whoops: "whoops";
$oops: "oops";
} */
Looks good. The type of values is what you want it to be, and the compiler allows the valid properties and complains about the invalid ones.
Playground link to code
This is the code I have right now:
extends Control
var _handles:Array = [];
var _handleSize:int = 10;
var _mouseOverHandleIndex = -1;
var _draggingHandleIndex = -1;
class Handle:
enum MovementConstraint { Horizontal, Vertical, Both, None}
var _movementConstraint: MovementConstraint = MovementConstraint.Both;
var _position: Vector2 = Vector2.ZERO;
func init(position:Vector2, movementConstraint: MovementConstraint):
_position = position;
_movementConstraint = MovementConstraint;
Godot parser is complaining about "The indentifier 'MovementConstraint' isn't a valid type (not a script or class), or couldn't be found on base self."
I have tried to move the enum outside (into he parent class space) but same thing happens.
The issue is that a GDScript enum is a collection of constants (and a dictionary to access them, if the enum is named). So there are not really variables which type is the enum. Instead they are int. And yes, that also means Godot won't complain if set a value that is not from the enum you expect (you could add validation by defining a custom setter with setget).
Let's have well known complex type (CT, e.g. Dto from the example).
Let's have set of type converters (functions) transforming value of one type to another (e.g. int SubDto2ToInt(SubDto2 sub), string GuidToString(Guid gd)).
Is there any method, how to configure Automapper and tell him:
"Map CT using these provided Type Converters and create object of anonymous type."?
This would effectively:
go over all CT's properties (recursively)
check the list of converters
if any present for given property type, convert and add to the result
otherwise proceed the default way (put it "as is" to the result)
I have had partial success when I pre-created the anonymous type, but that does not make much sense, because I can then create "normal" target type and define classic mapping. So the point is to let the Automapper create the target type on his own. The target member types would then be driven by either the source type or the conversion result type (if registered).
Example:
class SubDto1 { int M1; int M2; } // Pretend they are get/set properties
class SubDto2 { int N1; int N2; } // dtto
class Dto { SubDto1 Sub1; SubDto2 Sub2; Guid Id; } // dtto
var source = new Dto { Sub1 = new SubDto1 {M1 = 1, M2 = 2}, Sub2 = new SubDto2 {N1 = 10, N2 = 20}, Id = Guid.NewGuid()};
var expectedTargetEquivalentTo = new
{
Sub1 = new {M1 = 1, M1 = 2}, // No conversion registered here, "as is"
Sub2 = 30, // Fancy SubDto2 -> int conversion registered here
Id = "00000000-0000-0000-0000-000000000000", // Guid -> string conversion registered here
}
Problem
Is there a way in Typescript to define a type that is only a string literal, excluding string itself?
Note that I am not talking about a certain list of string literal; for which, a simple union of "Value1" | "Value2", or an enum type would work. I am talking about any string literal, but not string itself.
Example Code
type OnlyStringLiterals = ...; // <--- what should we put here?
const v1: OnlyStringLiterals = "hi"; // should work
const v2: OnlyStringLiterals = "bye"; // should work
// and so should be for any single string value assigned
// But:
const v3: OnlyStringLiterals = ("red" as string); // should NOT work -- it's string
Use Case
I am doing Branding on the types in my code, and I am passing a brand name, as a template, to my parent class. See the code below:
abstract class MyAbstractClass<
BRAND_T extends string,
VALUE_T = string
> {
constructor(private readonly _value: VALUE_T) { }
getValue(): VALUE_T { return this._value; }
private _Brand?: BRAND_T; // required to error on the last line, as intended!
}
class FirstName extends MyAbstractClass<"FirstName"> {
}
class AdminRole extends MyAbstractClass<"AdminRole"> {
}
class SubClassWithMissedName extends MyAbstractClass<string> {
// I want this to error! ........................ ^^^^^^
}
function printName(name: FirstName) {
console.log(name.getValue());
}
const userFirstName = new FirstName("Alex");
const userRole = new AdminRole("Moderator");
printName(userRole); // Already errors, as expected
Playground Link
I want to make sure every subclass is passing exactly a string literal, and not just string to the parent class.
I found an answer that works for my use case, but is not the most reusable one. Just sharing it anyway.
Thought Process
I believe it's not possible to have one solid type to represent what I wanted, because I cannot even think what will show up in VS Code if I hover over it!
However, to my knowledge, there is a function-style checking in Typescript for types that you can pass a type in and expect a type back, and finally assign a value to it to see if it goes through.
Type-checking using a Generic Type and a follow-up assignment
Using this technique I am thinking about the following template type:
type TrueStringLiterals<T extends string> = string extends T ? never : true;
const v1 = "hi";
const check1: TrueStringLiterals<typeof v1> = true; // No error :-)
const v2 = "bye";
const check2: TrueStringLiterals<typeof v2> = true; // No error :-)
const v3 = ("red" as string);
const check3: TrueStringLiterals<typeof v3> = true; // Errors, as expected!
Playground Link
Easier in an already-passed Generic Type
Also, in my use case, I am doing:
abstract class MyAbstractClass<
BRAND_T extends (string extends BRAND_T ? never : string),
VALUE_T = string
> {
...
Playground Link
... which works like a charm!
You can create utility type which will allow only on subset of string:
type SubString<T> = T extends string ?
string extends T ? never
: T
: never
const makeSubStr = <T extends string>(a: SubString<T>) => a
const a = makeSubStr('strLiteral')
const b = makeSubStr('strLiteral' as string) // error
const c: string = 'elo I am string'
const d = makeSubStr(c) // error
const e: SubString<"red"> = ("red" as string); // error
This type will also return never if something is not a string, in your answer TrueStringLiterals will not take this case into consideration and pass it through.
The other answers don't catch the case where the provided type parameter is a union of literal strings. If this shall be explicitly avoided, as could be read from the OPs question, the following solution, based on the other two can be used:
type UnUnion<T, S> = T extends S ? ([S] extends [T] ? T : never) : never;
type NotUnion<T> = UnUnion<T, T>;
type LiteralString<T extends string> = string extends T ? never : NotUnion<T>;
where UnUnion uses the fact that if T is a union, say 'a' | 'b', the union is distributed over the rest of the type expression.
(['a'|'b'] extends ['a'] ? ... ) | (['a'|'b'] extends ['b'] ? ...)
If T is a union, none of these can hold and all the parts turn into never.
NotUnion reduces this to have just one generic parameter and LiteralString just uses its result in case its parameter is not extendable by string.
Playground Link
I'd like to submit an answer from a similar question I recently asked, that is far more simple than the examples given so far:
type SpecificString<S extends Exclude<string, S>> = S
let test1: SpecificString<"a" | "b" | "c"> // okay
let test2: SpecificString<string> // error
//guaranteed to work where `Exclude<string, T>` wouldn't
let test3: Exclude<SpecificString<"a" | "1">, "1">
test3 = "a" // okay
test3 = "1" // error
Basically how this works:
Exclude<string, "any string literal"> ==> resolves to string
Exclude<string, string> ==> resolves to never
You can call this F-bounded quantification if you like I guess.
I'm trying to pass a Class reference and instantiate it in a function. This doesn't work:
function foo(myClassRef:Class):Void {
var myVar = new myClassRef();
}
foo(MyClass);
It gives Unexpected (.
Is this possible in Haxe 3?
Class has a Type Parameter, so if you're going to accept a class as an argument, you need to specify a type parameter.
Accept any class:
function foo(myClassRef:Class<Dynamic>):Void {
var myVar = Type.createInstance( myClassRef, [constructorArg1, constructorArg2....] );
trace( Type.typeof(myVar) );
}
Accept only "sys.db.Object" class or sub classes:
function foo(myClassRef:Class<sys.db.Object>):Void {
var myVar = Type.createInstance( myClassRef, [] );
trace( Type.typeof(myVar) );
}
Haxe 3 also allows generic functions:
#:generic function foo<T:Dynamic>(t:Class<T>) {
var myVar = new T();
trace( Type.typeof(myVar) );
}
Here you declare the function to be generic, which means that for each different type parameter, a different version of the function will be compiled. You accept Class, where T is the type parameter - in this case, dynamic, so it will work with any class. Finally, using generic functions let's you write new T(), which may seem a more natural syntax, and there may be performance benefits on some platforms.
It is possible in Haxe3 and Haxe2
function foo<T>(myClassRef:T):Void {
var myVar = new T();
}
Note: Haxe3 class (where foo is implemented) must be #:generic if you want new T() to work.
Haxe2 is another story:
function foo<T>(myClassRef:Class<T>):Void {
var myVar = Type.createEmptyInstance(Type.getClass(myClassRef));
}