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
How Are Exceptions Implemented Under the Hood
Confused by Squaring MACro Sqr in C
Can't Use Structure in Global Scope
What Is the Most Efficient Thread-Safe C++ Logger
What Are the Stages of Compilation of a C++ Program
What Does "-Wall" in "G++ -Wall Test.Cpp -O Test" Do
Learning to Work with Audio in C++
How to Make an Unordered Set of Pairs of Integers in C++
Set Breakpoint in C or C++ Code Programmatically for Gdb on Linux
When Does an Incomplete Type Error Occur in C++
Format Curly Braces on Same Line in C++ VScode
How to Implement Interfaces in C++
Classification of Detectors, Extractors and Matchers