Restrict Passed Parameter to a String Literal

Restrict passed parameter to a string literal

There is a way to force a string literal argument: make a user defined literal operator. You can make the operator constexpr to get the size at compile time:

constexpr Literal operator "" _suffix(char const* str, size_t len) {
return Literal(chars, len);
}

I don't know of any compiler that implements this feature at this time.

C++: Pass string literal or variable to function

The traditional way to take a read-only parameter is by const lvalue reference.

std::string f(const std::string& x)

This rule of thumb applies to many types, not just std::string. The primary exceptions are types that are not bigger than a pointer (e.g. a char).

It's rather unusual for a function to have a const rvalue reference. As you discovered, that adds difficulty when trying to pass a variable as the argument. A non-const rvalue reference has value, but a const rvalue reference is inferior to a const lvaue reference in most cases. See also Do rvalue references to const have any use?

How do I restrict passed variable to specific values in python?

With choices:

In [214]: parser = argparse.ArgumentParser()
In [215]: parser.add_argument('--foo', choices=['one','two','three','four']);

Accepted:

In [216]: parser.parse_args('--foo one'.split())
Out[216]: Namespace(foo='one')

rejected:

In [217]: parser.parse_args('--foo five'.split())
usage: ipython3 [-h] [--foo {one,two,three,four}]
ipython3: error: argument --foo: invalid choice: 'five' (choose from 'one', 'two', 'three', 'four')

help:

In [218]: parser.parse_args('-h'.split())
usage: ipython3 [-h] [--foo {one,two,three,four}]

optional arguments:
-h, --help show this help message and exit
--foo {one,two,three,four}

If I'd defined a metavar, the help will be

usage: ipython3 [-h] [--foo CHOICES]

optional arguments:
-h, --help show this help message and exit
--foo CHOICES

Or if the choices is too long, define a type function:

In [222]: def mychoices(astr):
...: if astr in ['one','two','three','four']:
...: return astr
...: else:
...: raise argparse.ArgumentTypeError('Wrong choice')

In [223]: parser = argparse.ArgumentParser()
In [224]: parser.add_argument('--foo', type=mychoices);

In [225]: parser.parse_args('--foo one'.split())
Out[225]: Namespace(foo='one')

In [226]: parser.parse_args('--foo five'.split())
usage: ipython3 [-h] [--foo FOO]
ipython3: error: argument --foo: Wrong choice

In [227]: parser.parse_args('-h'.split())
usage: ipython3 [-h] [--foo FOO]

optional arguments:
-h, --help show this help message and exit
--foo FOO

Restrict generic typescript type to single string literal value, disallowing unions

There is currently no direct way to restrict a generic type parameter to a single member of a union. There's an open feature request at microsoft/TypeScript#27808 to support something like T extends oneof EntityTyes, but that's not implemented yet. If you want to see it happen you might visit that issue and give it a , but I don't know it would have much effect.

That means T extends EntityTypes could allow T to be any subtype of EntityTypes, including the full EntityTypes union. In practice this tends not to be a huge deal, since usually such T does get inferred as a single member (people often call foo("x") or foo("y") instead of foo(Math.random()<0.5?"x":"y")). But sometimes it causes problems, especially with example code like yours.


So how can we work around this? Given your particular example code, I'd say that you want GenericEntity to actually be more like a discriminated union with three members, instead of a generic type. But you can get both, via a distributive object type as coined in microsoft/TypeScript#47109. It looks like this:

type GenericEntity<T extends EntityTypes = EntityTypes> = { [U in T]: {
type: U;
fieldProperty: EntityMappings[U];
} }[T]

We are taking the type T passed in and mapping over its members, and then indexing into it with T. This has no real effect if T is a single string literal, but when it's a union, the result is also a union without any of the undesirable "cross-correlated" terms:

type GE = GenericEntity;
/* type GE = {
type: "foo";
fieldProperty: string;
} | {
type: "bar";
fieldProperty: number;
} | {
type: "baz";
fieldProperty: string[];
} */

(I also made a generic parameter default for T so GenericEntity without a type argument is the full union we actually want here.)

So what we're doing is: instead of prohibiting unions in T, we are handling them by distributing over them.

Now things will behave as you desire:

const instance: GenericEntity<'foo'> = {
type: 'foo',
fieldProperty: 'hello',
} // okay;

const otherInstance: GenericEntity<'baz'> = {
type: 'baz',
fieldProperty: ['a', 'b', 'c'],
} // okay

const badInstance: GenericEntity<'foo' | 'baz'> = {
type: 'baz',
fieldProperty: 'blah',
}; // error!

Looks good!

Playground link to code

Template Non-Type argument, C++11, restriction for string literals

The closest that string literals come to being allowed is, from your question:

a constant expression (5.19) that designates the address of an object with static storage duration and external or internal linkage

String literals have neither external linkage nor internal linkage, so they aren't allowed.

If you have multiple translation units, each containing a definition of const char arr[5]; with internal linkage, then those are all distinct objects, with distinct addresses, but within a single translation unit, arr == arr, always. Implementations figured out how to make that work for template arguments.

If you have multiple translation units, each containing "1234", then those are not guaranteed to have distinct addresses. Yet even in a single translation unit, they are not guaranteed to have the same address.

If "1234" != "1234", then referring to a template S<"1234"> makes no sense: you'd be referring to a different template instantiation each time.

If "1234" == "1234", then it gets complicated for implementations to make sure that S<"1234"> is the same type in each translation unit.

Preventing object literals type widening when passed as argument in TypeScript

Yes, this is possible. But the solution might seem unintuitive and redundant.

You will have to add another generic type to the function. This will keep will allow us to keep narrowed type of string literals passed to the function.

function func<T extends Record<string, S>, S extends string>(value: T): T { 
return value;
};

let test = func({ key: 'value', a: "a" })
// let test: {
// key: "value";
// a: "a";
// }

We can apply this to your complex example.

declare function formatMessage<
Key extends keyof typeof messages,
Props extends { [key: string]: S },
S extends string
>(messageKey: Key, props?: Props)
:ReplaceValues<(typeof messages)[Key], NonNullable<typeof props>>;

let test4 = formatMessage("created", {name: "TestValue"})
// let test4: "TestValue was created successfully."

Playground


Here are some further resources which helped me with problems like these in the past.

  • Typescript: Type Inference on function arguments

  • Suggestion: Const contexts for generic type inference - This post describes a way to narrow down object literals of arbitrary shape.



Related Topics



Leave a reply



Submit