文字列値を持つ列挙型を作成する


262

enumTypeScriptでを作成するには、次のコードを使用できます。

enum e {
    hello = 1,
    world = 2
};

また、値には次の方法でアクセスできます。

e.hello;
e.world;

enum文字列値を持つを作成するにはどうすればよいですか?

enum e {
    hello = "hello", // error: cannot convert string to e
    world = "world"  // error 
};

回答:


409

TypeScript 2.4

今すぐ文字列列挙型があるので、コードは機能します:

enum E {
    hello = "hello",
    world = "world"
};

🌹

TypeScript 1.8

TypeScript 1.8以降では、文字列リテラル型を使用して、名前付き文字列値に信頼性があり安全なエクスペリエンスを提供できます(一部は列挙型が使用されています)。

type Options = "hello" | "world";
var foo: Options;
foo = "hello"; // Okay 
foo = "asdf"; // Error!

詳細:https : //www.typescriptlang.org/docs/handbook/advanced-types.html#string-literal-types

レガシーサポート

TypeScriptの列挙型は数値ベースです。

ただし、静的メンバーを持つクラスを使用できます。

class E
{
    static hello = "hello";
    static world = "world"; 
}

あなたも平易に行くことができます:

var E = {
    hello: "hello",
    world: "world"
}

更新:var test:E = E.hello;次の ようなことを行うことができるという要件に基づいて、これを満たします:

class E
{
    // boilerplate 
    constructor(public value:string){    
    }

    toString(){
        return this.value;
    }

    // values 
    static hello = new E("hello");
    static world = new E("world");
}

// Sample usage: 
var first:E = E.hello;
var second:E = E.world;
var third:E = E.hello;

console.log("First value is: "+ first);
console.log(first===third); 

少し改善:toString(): string { return this.value; }
psulek 2013

@psulek実はtypescriptですが、その推測されますtoString、それは返すので、返します文字列をthis.valueし、valueString型です。だからあなたはできませんvar x:number = E.hello.toString();、そしてあなたがそうするならvar x = E.hello.toString();xはタイプであると推測されますstringも同様れます:)
バサラト

2
@BASaratこれは、typescriptがこのようなケースを処理することには当てはまりますが、tsコンパイラに必要ではなくても、メソッド定義を見たときにコード作成者が知る必要があるとしても、それを知るたびに常に戻り値の型でメソッドを装飾してきた返すタイプ。
psulek

@basaratは、get()メソッドをreturn this.value?これにより、変換時だけでなく、アクセス時に文字列値が返されますtoString()
ジョン

@basaratそのようないくつかの「列挙型」がある場合、コンパイラーは構造型指定のためにそれらを区別しません-コンパイラーはvalueすべての型のメンバーを参照し、それらを比較可能な型として扱います。valueメンバーを非公開にすることもできます。この方法では、コンパイラーはそれを認識せず、構造型付けを適用しようとしません。
Kirill G.

113

TypeScriptの最新バージョン(1.0RC)では、次のような列挙型を使用できます。

enum States {
    New,
    Active,
    Disabled
} 

// this will show message '0' which is number representation of enum member
alert(States.Active); 

// this will show message 'Disabled' as string representation of enum member
alert(States[States.Disabled]);

アップデート1

文字列値から列挙型メンバーの数値を取得するには、これを使用できます:

var str = "Active";
// this will show message '1'
alert(States[str]);

アップデート2

最新のTypeScript 2.4では、次のような文字列列挙が導入されました。

enum ActionType {
    AddUser = "ADD_USER",
    DeleteUser = "DELETE_USER",
    RenameUser = "RENAME_USER",

    // Aliases
    RemoveUser = DeleteUser,
}

TypeScript 2.4の詳細については、MSDNのブログを参照してください。


2
一般に、このソリューションが推奨されます(実際の列挙型であるため)。ただし、列挙型の名前(つまり「文字列」)には非常に制約があります。
JasonS 2015

2
現在のところ最高のソリューション。
アロンアミール

2
これについて何か新しいことはありますか?States[str]今日は動作しないので。Type 'string' is not assignable to type 'States'
MrCroft 2016

1
@MrCroft次を使用できます。TypescriptのStates[str as any]現在の(2.x)バージョンで実行します。
psulek 2017

States [str]は私が探していたものです。ありがとう!
Martin Konicek

81

TypeScript 2.4以降

これで、列挙型メンバーに文字列値を直接割り当てることができます。

enum Season {
    Winter = "winter",
    Spring = "spring",
    Summer = "summer",
    Fall = "fall"
}

詳細については、#15486を参照してください。

TypeScript 1.8以降

TypeScript 1.8以降では、文字列リテラルタイプを作成して、値リストに同じ名前のタイプとオブジェクトを定義できます。これは、文字列列挙型の予想される動作を模倣しています。

次に例を示します。

type MyStringEnum = "member1" | "member2";

const MyStringEnum = {
    Member1: "member1" as MyStringEnum,
    Member2: "member2" as MyStringEnum
};

これは文字列列挙型のように機能します。

// implicit typing example
let myVariable = MyStringEnum.Member1; // ok
myVariable = "member2";                // ok
myVariable = "some other value";       // error, desired

// explict typing example
let myExplicitlyTypedVariable: MyStringEnum;
myExplicitlyTypedVariable = MyStringEnum.Member1; // ok
myExplicitlyTypedVariable = "member2";            // ok
myExplicitlyTypedVariable = "some other value";   // error, desired

オブジェクト内のすべての文字列を入力してください!そうしないと、上記の最初の例では、変数は暗黙的にに型指定されませんMyStringEnum


1
宣言ファイルで同様のものを定義するにはどうすればよいですか?
Zev Spitz 2016年

@ZevSpitzあなたはこれ
David Sherret

注目に値するのは、現在のコンパイラでは、MyStringEnumに文字列値を誤って入力しても問題がないことです。文字列が常に有効であることを確認するために、「エンフォーサ」インターフェースを作成しています。次に例を示します。interface MyStringEnumEnforcer {Member1:MyStringEnum、Member2:MyStringEnum} Then const MyStringEnum:MyStringEnumEnforcer = {Member1: "member1"、Member2: "member2"}これは、コンパイラが最終的に機能する可能性がありますが、誤って入力された文字列を許可しません最終的には元のシナリオ。このアプローチには多くの儀式がありますが、私は安全が好きです。
jmorc 2017


40

TypeScript 0.9.0.1では、コンパイラエラーが発生しますが、コンパイラはtsファイルをjsファイルにコンパイルできます。コードは期待どおりに機能し、Visual Studio 2012は自動コード補完をサポートできます。

更新:

構文では、TypeScriptでは文字列値を持つ列挙型を作成できませんが、コンパイラをハックできます:p

enum Link
{
    LEARN   =   <any>'/Tutorial',
    PLAY    =   <any>'/Playground',
    GET_IT  =   <any>'/#Download',
    RUN_IT  =   <any>'/Samples',
    JOIN_IN =   <any>'/#Community'
}

alert('Link.LEARN:    '                     + Link.LEARN);
alert('Link.PLAY:    '                      + Link.PLAY);
alert('Link.GET_IT:    '                    + Link.GET_IT);
alert('Link[\'/Samples\']:    Link.'        + Link['/Samples']);
alert('Link[\'/#Community\']    Link.'      + Link['/#Community']);

遊び場


1
素敵なハックですが、これらの列挙型/定数をスイッチステートメントで使用することはできません。たとえばcase Link.LEARN:Cannot convert 'Link.LEARN' to 'string'ビルドエラーが発生します。キャストは機能しません。
コーディング終了

@TrueBlueAussieこれは、TSC 1.0.0.0を実行している私には問題なく動作するようです。また、何らかの理由で、caseステートメントに文字列定数/変数を配置する必要がある場合は、anyにキャストすると機能します。
CodeAndCats 2014

1
また、@ zjc0816に感謝します。このソリューションが大好きです:)
CodeAndCats

それが私が欲しかった解決策です。
Murhaf Sousli 2016年

5
おかしい、TypeScriptが列挙型文字列を既にサポートしていないのはなぜでしょうか。
ヘンディイラワン

23

TypeScript 2.1以降

TypeScript 2.1で導入されたルックアップタイプでは、文字列列挙型をシミュレートする別のパターンを使用できます。

// String enums in TypeScript 2.1
const EntityType = {
    Foo: 'Foo' as 'Foo',
    Bar: 'Bar' as 'Bar'
};

function doIt(entity: keyof typeof EntityType) {
    // ...
}

EntityType.Foo          // 'Foo'
doIt(EntityType.Foo);   // 👍
doIt(EntityType.Bar);   // 👍
doIt('Foo');            // 👍
doIt('Bad');            // 🙁 

TypeScript 2.4以降

TypeScriptバージョン2.4では、文字列列挙型のネイティブサポートが導入されたため、上記のソリューションは必要ありません。TS docsから:

enum Colors {
  Red = "RED",
  Green = "GREEN",
  Blue = "BLUE",
}

列挙キー名が文字列値と異なる場合(たとえば、非常に長いため)、どうすればよいですか?
CletusW 2017

気にしないで!下記のルカシュ・pniewskiさん@答えで解決stackoverflow.com/a/42820134/1431146
CletusW

Enumを逆マッピングしようとすると、tslintはそのString-Enumの例でエラーをスローします。要素は暗黙的に「any」タイプを持ちます。これは、インデックス式がタイプ「number」ではないためです。私は問題がTS文字列の列挙型で、逆マッピングすることで、文字列、列挙型の例でコメントを見ることができないということだと思いtypescriptlang.org/docs/handbook/release-notes/...を -これは、TS 2.4のために真であるように思われますString-Enumが導入されましたが、TS 2.6.2でもエラーが発生します。例:機能しColors["RED"]ません。これを解決するためのアイデア(JSON変換に必要)。
masi

19

列挙型の文字列にアクセスするネイティブな方法を使用しないのはなぜですか。

enum e {
  WHY,
  NOT,
  USE,
  NATIVE
}

e[e.WHY] // this returns string 'WHY'

2
これが私が探していた答えです、ありがとう!他の解決策は巧妙な回避策ですが、これはとても簡単です:)
M--

19
これは質問の答えにはなりません。問題は、列挙型の文字列へのアクセスについてではありません。enum Why { Because = "You Can't", Always = "Do Things That Way." };)
James Wilkins 2016年

数値列挙型を使用する場合、0は偽である、デバッグが難しいなどの問題があります
robmcm

@robmcm solveed enum e {WHY = 1、NOT = 2、USE = 3、NATIVE = 4} e [e.WHY] //これは文字列 'WHY'を返す
Mient-jan Stelling

16

最新のTypeScriptでは文字列列挙型を使用できます。

enum e
{
    hello = <any>"hello",
    world = <any>"world"
};

出典:https : //blog.rsuter.com/how-to-implement-an-enum-with-string-values-in-typescript/


更新-2016

最近Reactで使用する文字列のセットを作成する、もう少し堅牢な方法は次のとおりです。

export class Messages
{
    static CouldNotValidateRequest: string = 'There was an error validating the request';
    static PasswordMustNotBeBlank: string = 'Password must not be blank';   
}

import {Messages as msg} from '../core/messages';
console.log(msg.PasswordMustNotBeBlank);

1
これは私にとって最も簡潔な方法でした...少なくともTS 1.8でコンパイルするために足場を更新する方法がわかるまで
ThinkBonobo

ただし、これに関する1つの問題は<string>e.hello、エラーをトリガーすることです。e.helloコンパイラーはまだ数値と見なします。<number>e.helloでも動作します。これを回避する方法はありますか?私が考えることができるすべては<string><any>e.helloです。
RainingChain 2016年

もう1つの問題は、enumメンバーがenum値と等しい場合です。例:enum Test { a = <any>"b", b = <any>"c", c = <any>"a" } Test.a === 'c'
RainingChain 2016年

私はいつもこの方法を使っています。ストリング列挙型のロック。コンパイラが文字列リテラルのファーストクラスサポートを備えていないのは残念ですが、セカンドクラスサポートは備えています。コンパイラは<any>ハックをいつ使用したかを実際に認識します。これにより、.d.tsファイルで<any>ハックを使用できなくなります。コンパイラは明らかに認識しているので、この「ハック」の使用に正当性を与えます。それの完全にそれを止めません。
CodeAndCats

ところで、あなたはにキャストというし、文字列の列挙型の値を文字列値を比較する場合<any>に、その後<string>ちょうど行い、:someStringValue == someEnumValue.toString()
CodeAndCats

10

TypeScript 2.0を使用して継承できるかなりクリーンなソリューションを次に示します。以前のバージョンではこれを試していません。

ボーナス:値はどのタイプでもかまいません

export class Enum<T> {
  public constructor(public readonly value: T) {}
  public toString() {
    return this.value.toString();
  }
}

export class PrimaryColor extends Enum<string> {
  public static readonly Red = new Enum('#FF0000');
  public static readonly Green = new Enum('#00FF00');
  public static readonly Blue = new Enum('#0000FF');
}

export class Color extends PrimaryColor {
  public static readonly White = new Enum('#FFFFFF');
  public static readonly Black = new Enum('#000000');
}

// Usage:

console.log(PrimaryColor.Red);
// Output: Enum { value: '#FF0000' }
console.log(Color.Red); // inherited!
// Output: Enum { value: '#FF0000' }
console.log(Color.Red.value); // we have to call .value to get the value.
// Output: #FF0000
console.log(Color.Red.toString()); // toString() works too.
// Output: #FF0000

class Thing {
  color: Color;
}

let thing: Thing = {
  color: Color.Red,
};

switch (thing.color) {
  case Color.Red: // ...
  case Color.White: // ...
}

1
正解です。継承をサポートするEnumのようなオブジェクトを作成するのに苦労していました。
DanielM 2016年

クラスベースの列挙型を使用した例:goo.gl/SwH4zb(TypeScriptの遊び場へのリンク)。
DanielM 2016年

8

これへのハックな方法は次のとおりです:-

CallStatus.ts

enum Status
{
    PENDING_SCHEDULING,
    SCHEDULED,
    CANCELLED,
    COMPLETED,
    IN_PROGRESS,
    FAILED,
    POSTPONED
}

export = Status

Utils.ts

static getEnumString(enum:any, key:any):string
{
    return enum[enum[key]];
}

使い方

Utils.getEnumString(Status, Status.COMPLETED); // = "COMPLETED"

7

これは私にとってはうまくいきます:

class MyClass {
    static MyEnum: { Value1; Value2; Value3; }
    = {
        Value1: "Value1",
        Value2: "Value2",
        Value3: "Value3"
    };
}

または

module MyModule {
    export var MyEnum: { Value1; Value2; Value3; }
    = {
        Value1: "Value1",
        Value2: "Value2",
        Value3: "Value3"
    };
}

8)

更新:これを投稿した直後に別の方法を発見しましたが、更新を投稿するのを忘れていました(ただし、誰かがすでに上記で言及していました)。

enum MyEnum {
    value1 = <any>"value1 ", 
    value2 = <any>"value2 ", 
    value3 = <any>"value3 " 
}

4

私はインターフェイスを宣言し、その型の変数を使用して列挙型にアクセスします。TypeScriptは列挙型で何かが変更されると不平を言うので、インターフェイスと列挙型を同期させることは実際には簡単です。

エラーTS2345:タイプ 'typeof EAbFlagEnum'の引数は、タイプ 'IAbFlagEnum'のパラメーターに割り当てることができません。タイプ 'typeof EAbFlagEnum'にプロパティ 'Move'がありません。

このメソッドの利点は、さまざまな状況で列挙型(インターフェース)を使用するために型キャストが必要ないため、スイッチ/ケースなど、より多くのタイプの状況がサポートされることです。

// Declare a TypeScript enum using unique string 
//  (per hack mentioned by zjc0816)

enum EAbFlagEnum {
  None      = <any> "none",
  Select    = <any> "sel",
  Move      = <any> "mov",
  Edit      = <any> "edit",
  Sort      = <any> "sort",
  Clone     = <any> "clone"
}

// Create an interface that shadows the enum
//   and asserts that members are a type of any

interface IAbFlagEnum {
    None:   any;
    Select: any;
    Move:   any;
    Edit:   any;
    Sort:   any;
    Clone:  any;
}

// Export a variable of type interface that points to the enum

export var AbFlagEnum: IAbFlagEnum = EAbFlagEnum;

列挙型ではなく変数を使用すると、望ましい結果が得られます。

var strVal: string = AbFlagEnum.Edit;

switch (strVal) {
  case AbFlagEnum.Edit:
    break;
  case AbFlagEnum.Move:
    break;
  case AbFlagEnum.Clone
}

フラグは私にとってもう1つの必需品だったので、この例に追加してテストを含むNPMモジュールを作成しました。

https://github.com/djabraham/ts-enum-tools


これは、定義とインポートを混在させることができる唯一の答えです。いいね!export default EAbFlagEnum as IAbFlagEnum;変数を再宣言する代わりに使用できます。<any>列挙型のキャストも削除しましたが、正常に機能します。
ギヨームF.

4

更新:TypeScript 3.4

あなたは単に使うことができますas const

const AwesomeType = {
   Foo: "foo",
   Bar: "bar"
} as const;

TypeScript 2.1

これもこの方法で行うことができます。それが誰かを助けることを願っています。

const AwesomeType = {
    Foo: "foo" as "foo",
    Bar: "bar" as "bar"
};

type AwesomeType = (typeof AwesomeType)[keyof typeof AwesomeType];

console.log(AwesomeType.Bar); // returns bar
console.log(AwesomeType.Foo); // returns foo

function doSth(awesometype: AwesomeType) {
    console.log(awesometype);
}

doSth("foo") // return foo
doSth("bar") // returns bar
doSth(AwesomeType.Bar) // returns bar
doSth(AwesomeType.Foo) // returns foo
doSth('error') // does not compile

これはまさに私が必要としたものです!これは、大文字と小文字の違いで示したように、キー名を文字列値とは異なるものにすることをサポートしています。ありがとう!
CletusW 2017

2

カスタムトランスフォーマー(https://github.com/Microsoft/TypeScript/pull/13940typescript @ nextで利用できる)を使用すると、文字列リテラルタイプの文字列値を持つオブジェクトのような列挙型を作成できます。

私のnpmパッケージ、ts-transformer-enumerateを調べてください

使用例:

// The signature of `enumerate` here is `function enumerate<T extends string>(): { [K in T]: K };`
import { enumerate } from 'ts-transformer-enumerate';

type Colors = 'green' | 'yellow' | 'red';
const Colors = enumerate<Colors>();

console.log(Colors.green); // 'green'
console.log(Colors.yellow); // 'yellow'
console.log(Colors.red); // 'red'

2

TypeScript <2.4

/** Utility function to create a K:V from a list of strings */
function strEnum<T extends string>(o: Array<T>): {[K in T]: K} {
  return o.reduce((res, key) => {
    res[key] = key;
    return res;
  }, Object.create(null));
}

/**
  * Sample create a string enum
  */

/** Create a K:V */
const Direction = strEnum([
  'North',
  'South',
  'East',
  'West'
])
/** Create a Type */
type Direction = keyof typeof Direction;

/** 
  * Sample using a string enum
  */
let sample: Direction;

sample = Direction.North; // Okay
sample = 'North'; // Okay
sample = 'AnythingElse'; // ERROR!

から https://basarat.gitbooks.io/typescript/docs/types/literal-types.html

ソースリンクには、文字列リテラルタイプを実現するためのより簡単な方法があります。


2

答えはたくさんありますが、完全な解決策はありません。受け入れられた回答との問題は、enum { this, one }多くのファイルでたまたま使用している文字列値が分散されることです。「更新」もあまり好きではありません。複雑で、型も活用していません。マイケル・ブロムリーの答えは最も正しいと思いますが、そのインターフェースは少し面倒であり、型で行うことができます。

TypeScript 2.0を使用しています。*これが私がすることです

export type Greeting = "hello" | "world";
export const Greeting : { hello: Greeting , world: Greeting } = {
    hello: "hello",
    world: "world"
};

let greet: Greeting = Greeting.hello

また、便利なIDEを使用すると、より適切なタイプ/ホバーオーバー情報も表示されます。欠点は、文字列を2回書き込む必要があることですが、少なくとも2か所にしかありません。


1

@バサラトの答えは素晴らしかった。以下は単純化されていますが、使用できる少し拡張された例です。

export type TMyEnumType = 'value1'|'value2';

export class MyEnumType {
    static VALUE1: TMyEnumType = 'value1';
    static VALUE2: TMyEnumType = 'value2';
}

console.log(MyEnumType.VALUE1); // 'value1'

const variable = MyEnumType.VALUE2; // it has the string value 'value2'

switch (variable) {
    case MyEnumType.VALUE1:
        // code...

    case MyEnumType.VALUE2:
        // code...
}

1

TypeScript 1.0.1でこの問題に最近直面し、この方法で解決しました:

enum IEvents {
        /** A click on a product or product link for one or more products. */
        CLICK,
        /** A view of product details. */
        DETAIL,
        /** Adding one or more products to a shopping cart. */
        ADD,
        /** Remove one or more products from a shopping cart. */
        REMOVE,
        /** Initiating the checkout process for one or more products. */
        CHECKOUT,
        /** Sending the option value for a given checkout step. */
        CHECKOUT_OPTION,
        /** The sale of one or more products. */
        PURCHASE,
        /** The refund of one or more products. */
        REFUND,
        /** A click on an internal promotion. */
        PROMO_CLICK
}

var Events = [
        'click',
        'detail',
        'add',
        'remove',
        'checkout',
        'checkout_option',
        'purchase',
        'refund',
        'promo_click'
];

function stuff(event: IEvents):boolean {
        // event can now be only IEvents constants
        Events[event]; // event is actually a number that matches the index of the array
}
// stuff('click') won't work, it needs to be called using stuff(IEvents.CLICK)

0

これを試してみるべきだと思います。この場合、変数の値は変更されず、列挙型のように機能します。クラスのように使用しても機能します唯一の欠点は、誤って静的変数の値を変更できることです。列挙型では必要ありません。

namespace portal {

export namespace storageNames {

    export const appRegistration = 'appRegistration';
    export const accessToken = 'access_token';

  }
}

0
export enum PaymentType {
                Cash = 1,
                Credit = 2
            }
var paymentType = PaymentType[PaymentType.Cash];

0
//to access the enum with its string value you can convert it to object 
//then you can convert enum to object with proberty 
//for Example :

enum days { "one" =3, "tow", "Three" }

let _days: any = days;

if (_days.one == days.one)
{ 
    alert(_days.one + ' | ' + _days[4]);
}


0

あなたが望むのは主に簡単なデバッグ(かなりタイプチェックを伴う)であり、列挙に特別な値を指定する必要がない場合、これは私がやっていることです:

export type Enum = { [index: number]: string } & { [key: string]: number } | Object;

/**
 * inplace update
 * */
export function enum_only_string<E extends Enum>(e: E) {
  Object.keys(e)
    .filter(i => Number.isFinite(+i))
    .forEach(i => {
      const s = e[i];
      e[s] = s;
      delete e[i];
    });
}

enum AuthType {
  phone, email, sms, password
}
enum_only_string(AuthType);

レガシーコード/データストレージをサポートする場合は、数値キーを保持することができます。

これにより、値を2回入力する手間を省くことができます。


0

文字列付きの非常に、非常に、非常に単純なEnum(TypeScript 2.4)

import * from '../mylib'

export enum MESSAGES {
    ERROR_CHART_UNKNOWN,
    ERROR_2
}

export class Messages {
    public static get(id : MESSAGES){
        let message = ""
        switch (id) {
            case MESSAGES.ERROR_CHART_UNKNOWN :
                message = "The chart does not exist."
                break;
            case MESSAGES.ERROR_2 :
                message = "example."
                break;
        }
        return message
    }
}

function log(messageName:MESSAGES){
    console.log(Messages.get(messageName))
}

0

以下のようにTypeScript 1.5で試しましたが、うまくいきました

module App.Constants {
   export enum e{
        Hello= ("Hello") as any,
World= ("World") as any
    }
}

0

typescript enums(v2.5)で説明を実装する方法を探していたところ、このパターンがうまくいきました。

export enum PriceTypes {
    Undefined = 0,
    UndefinedDescription = 'Undefined' as any,
    UserEntered = 1,
    UserEnteredDescription = 'User Entered' as any,
    GeneratedFromTrade = 2,
    GeneratedFromTradeDescription = 'Generated From Trade' as any,
    GeneratedFromFreeze = 3,
    GeneratedFromFreezeDescription = 'Generated Rom Freeze' as any
}

...

    GetDescription(e: any, id: number): string {
        return e[e[id].toString() + "Description"];
    }
    getPriceTypeDescription(price: IPricePoint): string {
        return this.GetDescription(PriceTypes, price.priceType);
    }

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.